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