Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4957faad authored by William Allen Simpson's avatar William Allen Simpson Committed by David S. Miller
Browse files

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: default avatar <William.Allen.Simpson@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bd0388ae
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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);


+3 −2
Original line number Original line Diff line number Diff line
@@ -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);
+65 −10
Original line number Original line Diff line number Diff line
@@ -3698,7 +3698,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
 * 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 @@ 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,
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 @@ 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,
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);


+43 −4
Original line number Original line Diff line number Diff line
@@ -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);

	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)
	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);
+8 −6
Original line number Original line Diff line number Diff line
@@ -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;
Loading