diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index e64f4c6..c8f4017 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -185,22 +185,37 @@ struct tcp_md5sig {
 #define TCP_COOKIE_PAIR_SIZE	(2*TCP_COOKIE_MAX)
 
 /* Flags for both getsockopt and setsockopt */
-#define TCP_COOKIE_IN_ALWAYS	(1 << 0)	/* Discard SYN without cookie */
-#define TCP_COOKIE_OUT_NEVER	(1 << 1)	/* Prohibit outgoing cookies,
+#define TCPCT_IN_ALWAYS		(1 << 0)	/* Discard SYN without cookie */
+#define TCPCT_OUT_NEVER		(1 << 1)	/* Prohibit outgoing cookies,
 						 * supercedes everything. */
-
-/* Flags for getsockopt */
-#define TCP_S_DATA_IN		(1 << 2)	/* Was data received? */
-#define TCP_S_DATA_OUT		(1 << 3)	/* Was data sent? */
-
-/* TCP_COOKIE_TRANSACTIONS data */
+#define TCPCT_IN_DATA		(1 << 2)	/* Was data received? */
+#define TCPCT_OUT_DATA		(1 << 3)	/* Was data sent? */
+/* reserved for future use: bits 4 .. 6 */
+#define TCPCT_EXTEND		(1 << 7)
+
+/* Extended Option flags for both getsockopt and setsockopt */
+#define TCPCT_EXTEND_SIZE	(0x7)		/* mask */
+#define TCPCT_EXTEND_TS32	(0x1)		/* default */
+#define TCPCT_EXTEND_TS64	(0x2)
+#define TCPCT_EXTEND_TS128	(0x4)
+
+/* TCP_COOKIE_TRANSACTIONS socket option header */
 struct tcp_cookie_transactions {
 	__u16	tcpct_flags;			/* see above */
-	__u8	__tcpct_pad1;			/* zero */
+	__u8	tcpct_extended;
 	__u8	tcpct_cookie_desired;		/* bytes */
 	__u16	tcpct_s_data_desired;		/* bytes of variable data */
 	__u16	tcpct_used;			/* bytes in value */
-	__u8	tcpct_value[TCP_MSS_DEFAULT];
+};
+
+struct tcpct_full {
+	struct tcp_cookie_transactions soh;
+	__u8	tcpct_value[TCP_COOKIE_PAIR_SIZE];
+};
+
+struct tcpct_half {
+	struct tcp_cookie_transactions soh;
+	__u8	tcpct_value[TCP_COOKIE_MAX];
 };
 
 #ifdef __KERNEL__
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 6c11eec..a5c7933 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2143,25 +2143,14 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 	case TCP_COOKIE_TRANSACTIONS: {
 		struct tcp_cookie_transactions ctd;
 		struct tcp_cookie_values *cvp = NULL;
+		int s_data_used = 0;
 
 		if (sizeof(ctd) > optlen)
 			return -EINVAL;
 		if (copy_from_user(&ctd, optval, sizeof(ctd)))
 			return -EFAULT;
 
-		if (ctd.tcpct_used > sizeof(ctd.tcpct_value) ||
-		    ctd.tcpct_s_data_desired > TCP_MSS_DESIRED)
-			return -EINVAL;
-
-		if (ctd.tcpct_cookie_desired == 0) {
-			/* default to global value */
-		} else if ((0x1 & ctd.tcpct_cookie_desired) ||
-			   ctd.tcpct_cookie_desired > TCP_COOKIE_MAX ||
-			   ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) {
-			return -EINVAL;
-		}
-
-		if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) {
+		if (TCPCT_OUT_NEVER & ctd.tcpct_flags) {
 			/* Supercedes all other values */
 			lock_sock(sk);
 			if (tp->cookie_values != NULL) {
@@ -2175,6 +2164,41 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 			return err;
 		}
 
+		if (ctd.tcpct_cookie_desired == 0) {
+			/* default to global value */
+		} else if ((0x1 & ctd.tcpct_cookie_desired) ||
+			   ctd.tcpct_cookie_desired > TCP_COOKIE_MAX ||
+			   ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) {
+			return -EINVAL;
+		}
+
+		if (ctd.tcpct_used > 0) {
+			if (ctd.tcpct_used + sizeof(ctd) > optlen)
+				return -EINVAL;
+			if (TCPCT_OUT_DATA & ctd.tcpct_flags) {
+				if (ctd.tcpct_used >
+						sysctl_tcp_syn_ack_data_limit)
+					return -EINVAL;
+				if (ctd.tcpct_s_data_desired > 0)
+					return -EINVAL;
+				s_data_used = ctd.tcpct_used;
+			} else {
+				if (ctd.tcpct_used > TCP_COOKIE_PAIR_SIZE)
+					return -EINVAL;
+				if (ctd.tcpct_used !=
+						ctd.tcpct_cookie_desired &&
+				    ctd.tcpct_used !=
+						ctd.tcpct_cookie_desired * 2)
+					return -EINVAL;
+				if (ctd.tcpct_s_data_desired >
+						sysctl_tcp_syn_data_limit)
+					return -EINVAL;
+			}
+		} else if (TCPCT_OUT_DATA & ctd.tcpct_flags) {
+			/* unexpected flag without s_data */
+			return -EINVAL;
+		}
+
 		/* Allocate ancillary memory before locking.
 		 */
 		if (ctd.tcpct_used > 0 ||
@@ -2182,7 +2206,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 		     (sysctl_tcp_cookie_size > 0 ||
 		      ctd.tcpct_cookie_desired > 0 ||
 		      ctd.tcpct_s_data_desired > 0))) {
-			cvp = kzalloc(sizeof(*cvp) + ctd.tcpct_used,
+			cvp = kzalloc(sizeof(*cvp) + s_data_used,
 				      GFP_KERNEL);
 			if (cvp == NULL)
 				return -ENOMEM;
@@ -2191,7 +2215,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 		}
 		lock_sock(sk);
 		tp->rx_opt.cookie_in_always =
-			(TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags);
+			(TCPCT_IN_ALWAYS & ctd.tcpct_flags);
 		tp->rx_opt.cookie_out_never = 0; /* false */
 
 		if (tp->cookie_values != NULL) {
@@ -2210,11 +2234,27 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 		if (cvp != NULL) {
 			cvp->cookie_desired = ctd.tcpct_cookie_desired;
 
-			if (ctd.tcpct_used > 0) {
-				memcpy(cvp->s_data_payload, ctd.tcpct_value,
-				       ctd.tcpct_used);
-				cvp->s_data_desired = ctd.tcpct_used;
+			if (s_data_used > 0) {
+				if (copy_from_user(cvp->s_data_payload,
+						   optval + sizeof(ctd),
+						   s_data_used)) {
+					kref_put(&cvp->kref,
+						 tcp_cookie_values_release);
+					return -EFAULT;
+				}
+				cvp->s_data_desired = s_data_used;
 				cvp->s_data_constant = 1; /* true */
+			} else if (ctd.tcpct_used > 0) {
+				if (copy_from_user(cvp->cookie_pair,
+						   optval + sizeof(ctd),
+						   ctd.tcpct_used)) {
+					kref_put(&cvp->kref,
+						 tcp_cookie_values_release);
+					return -EFAULT;
+				}
+				/* No constant payload data. */
+				cvp->s_data_desired = ctd.tcpct_s_data_desired;
+				cvp->s_data_constant = 0; /* false */
 			} else {
 				/* No constant payload data. */
 				cvp->s_data_desired = ctd.tcpct_s_data_desired;
@@ -2574,7 +2614,7 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
 		return 0;
 
 	case TCP_COOKIE_TRANSACTIONS: {
-		struct tcp_cookie_transactions ctd;
+		struct tcpct_full ctd;
 		struct tcp_cookie_values *cvp = tp->cookie_values;
 
 		if (get_user(len, optlen))
@@ -2583,23 +2623,23 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
 			return -EINVAL;
 
 		memset(&ctd, 0, sizeof(ctd));
-		ctd.tcpct_flags = (tp->rx_opt.cookie_in_always ?
-				   TCP_COOKIE_IN_ALWAYS : 0)
-				| (tp->rx_opt.cookie_out_never ?
-				   TCP_COOKIE_OUT_NEVER : 0);
+		ctd.soh.tcpct_flags = (tp->rx_opt.cookie_in_always ?
+				       TCPCT_IN_ALWAYS : 0)
+				    | (tp->rx_opt.cookie_out_never ?
+				       TCPCT_OUT_NEVER : 0);
 
 		if (cvp != NULL) {
-			ctd.tcpct_flags |= (cvp->s_data_in ?
-					    TCP_S_DATA_IN : 0)
-					 | (cvp->s_data_out ?
-					    TCP_S_DATA_OUT : 0);
+			ctd.soh.tcpct_flags |= (cvp->s_data_in ?
+						TCPCT_IN_DATA : 0)
+					     | (cvp->s_data_out ?
+						TCPCT_OUT_DATA : 0);
 
-			ctd.tcpct_cookie_desired = cvp->cookie_desired;
-			ctd.tcpct_s_data_desired = cvp->s_data_desired;
+			ctd.soh.tcpct_cookie_desired = cvp->cookie_desired;
+			ctd.soh.tcpct_s_data_desired = cvp->s_data_desired;
 
 			memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0],
 			       cvp->cookie_pair_size);
-			ctd.tcpct_used = cvp->cookie_pair_size;
+			ctd.soh.tcpct_used = cvp->cookie_pair_size;
 		}
 
 		if (put_user(sizeof(ctd), optlen))
-- 
1.7.1