diff options
-rw-r--r-- | include/linux/tcp.h | 1 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 71 |
2 files changed, 72 insertions, 0 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4e90e6ae79df..986593685566 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h | |||
@@ -109,6 +109,7 @@ enum { | |||
109 | #define TCP_REPAIR 19 /* TCP sock is under repair right now */ | 109 | #define TCP_REPAIR 19 /* TCP sock is under repair right now */ |
110 | #define TCP_REPAIR_QUEUE 20 | 110 | #define TCP_REPAIR_QUEUE 20 |
111 | #define TCP_QUEUE_SEQ 21 | 111 | #define TCP_QUEUE_SEQ 21 |
112 | #define TCP_REPAIR_OPTIONS 22 | ||
112 | 113 | ||
113 | enum { | 114 | enum { |
114 | TCP_NO_QUEUE, | 115 | TCP_NO_QUEUE, |
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b4e690ddb08c..3ce3bd031f33 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -2218,6 +2218,68 @@ static inline int tcp_can_repair_sock(struct sock *sk) | |||
2218 | ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); | 2218 | ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); |
2219 | } | 2219 | } |
2220 | 2220 | ||
2221 | static int tcp_repair_options_est(struct tcp_sock *tp, char __user *optbuf, unsigned int len) | ||
2222 | { | ||
2223 | /* | ||
2224 | * Options are stored in CODE:VALUE form where CODE is 8bit and VALUE | ||
2225 | * fits the respective TCPOLEN_ size | ||
2226 | */ | ||
2227 | |||
2228 | while (len > 0) { | ||
2229 | u8 opcode; | ||
2230 | |||
2231 | if (get_user(opcode, optbuf)) | ||
2232 | return -EFAULT; | ||
2233 | |||
2234 | optbuf++; | ||
2235 | len--; | ||
2236 | |||
2237 | switch (opcode) { | ||
2238 | case TCPOPT_MSS: { | ||
2239 | u16 in_mss; | ||
2240 | |||
2241 | if (len < sizeof(in_mss)) | ||
2242 | return -ENODATA; | ||
2243 | if (get_user(in_mss, optbuf)) | ||
2244 | return -EFAULT; | ||
2245 | |||
2246 | tp->rx_opt.mss_clamp = in_mss; | ||
2247 | |||
2248 | optbuf += sizeof(in_mss); | ||
2249 | len -= sizeof(in_mss); | ||
2250 | break; | ||
2251 | } | ||
2252 | case TCPOPT_WINDOW: { | ||
2253 | u8 wscale; | ||
2254 | |||
2255 | if (len < sizeof(wscale)) | ||
2256 | return -ENODATA; | ||
2257 | if (get_user(wscale, optbuf)) | ||
2258 | return -EFAULT; | ||
2259 | |||
2260 | if (wscale > 14) | ||
2261 | return -EFBIG; | ||
2262 | |||
2263 | tp->rx_opt.snd_wscale = wscale; | ||
2264 | |||
2265 | optbuf += sizeof(wscale); | ||
2266 | len -= sizeof(wscale); | ||
2267 | break; | ||
2268 | } | ||
2269 | case TCPOPT_SACK_PERM: | ||
2270 | tp->rx_opt.sack_ok |= TCP_SACK_SEEN; | ||
2271 | if (sysctl_tcp_fack) | ||
2272 | tcp_enable_fack(tp); | ||
2273 | break; | ||
2274 | case TCPOPT_TIMESTAMP: | ||
2275 | tp->rx_opt.tstamp_ok = 1; | ||
2276 | break; | ||
2277 | } | ||
2278 | } | ||
2279 | |||
2280 | return 0; | ||
2281 | } | ||
2282 | |||
2221 | /* | 2283 | /* |
2222 | * Socket option code for TCP. | 2284 | * Socket option code for TCP. |
2223 | */ | 2285 | */ |
@@ -2426,6 +2488,15 @@ static int do_tcp_setsockopt(struct sock *sk, int level, | |||
2426 | err = -EINVAL; | 2488 | err = -EINVAL; |
2427 | break; | 2489 | break; |
2428 | 2490 | ||
2491 | case TCP_REPAIR_OPTIONS: | ||
2492 | if (!tp->repair) | ||
2493 | err = -EINVAL; | ||
2494 | else if (sk->sk_state == TCP_ESTABLISHED) | ||
2495 | err = tcp_repair_options_est(tp, optval, optlen); | ||
2496 | else | ||
2497 | err = -EPERM; | ||
2498 | break; | ||
2499 | |||
2429 | case TCP_CORK: | 2500 | case TCP_CORK: |
2430 | /* When set indicates to always queue non-full frames. | 2501 | /* When set indicates to always queue non-full frames. |
2431 | * Later the user clears this option and we transmit | 2502 | * Later the user clears this option and we transmit |