diff options
author | William Allen Simpson <william.allen.simpson@gmail.com> | 2009-12-02 13:17:05 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-12-03 01:07:25 -0500 |
commit | 435cf559f02ea3a3159eb316f97dc88bdebe9432 (patch) | |
tree | 0b2a7e9110c46b193176b0a59fe5689eae7c18f3 | |
parent | 519855c508b9a17878c0977a3cdefc09b59b30df (diff) |
TCPCT part 1d: define TCP cookie option, extend existing struct's
Data structures are carefully composed to require minimal additions.
For example, the struct tcp_options_received cookie_plus variable fits
between existing 16-bit and 8-bit variables, requiring no additional
space (taking alignment into consideration). There are no additions to
tcp_request_sock, and only 1 pointer in tcp_sock.
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
The principle difference is using a TCP option to carry the cookie nonce,
instead of a user configured offset in the data. This is more flexible and
less subject to user configuration error. Such a cookie option has been
suggested for many years, and is also useful without SYN data, allowing
several related concepts to use the same extension option.
"Re: SYN floods (was: does history repeat itself?)", September 9, 1996.
http://www.merit.net/mail.archives/nanog/1996-09/msg00235.html
"Re: what a new TCP header might look like", May 12, 1998.
ftp://ftp.isi.edu/end2end/end2end-interest-1998.mail
These functions will also be used in subsequent patches that implement
additional features.
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
Signed-off-by: William.Allen.Simpson@gmail.com
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/tcp.h | 29 | ||||
-rw-r--r-- | include/net/tcp.h | 83 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 20 | ||||
-rw-r--r-- | net/ipv4/tcp_minisocks.c | 46 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 13 |
5 files changed, 177 insertions, 14 deletions
diff --git a/include/linux/tcp.h b/include/linux/tcp.h index eaa3113b3786..7fee8a4df931 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h | |||
@@ -247,31 +247,38 @@ struct tcp_options_received { | |||
247 | sack_ok : 4, /* SACK seen on SYN packet */ | 247 | sack_ok : 4, /* SACK seen on SYN packet */ |
248 | snd_wscale : 4, /* Window scaling received from sender */ | 248 | snd_wscale : 4, /* Window scaling received from sender */ |
249 | rcv_wscale : 4; /* Window scaling to send to receiver */ | 249 | rcv_wscale : 4; /* Window scaling to send to receiver */ |
250 | /* SACKs data */ | 250 | u8 cookie_plus:6, /* bytes in authenticator/cookie option */ |
251 | cookie_out_never:1, | ||
252 | cookie_in_always:1; | ||
251 | u8 num_sacks; /* Number of SACK blocks */ | 253 | u8 num_sacks; /* Number of SACK blocks */ |
252 | u16 user_mss; /* mss requested by user in ioctl */ | 254 | u16 user_mss; /* mss requested by user in ioctl */ |
253 | u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ | 255 | u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ |
254 | }; | 256 | }; |
255 | 257 | ||
256 | static inline void tcp_clear_options(struct tcp_options_received *rx_opt) | 258 | static inline void tcp_clear_options(struct tcp_options_received *rx_opt) |
257 | { | 259 | { |
258 | rx_opt->tstamp_ok = rx_opt->sack_ok = rx_opt->wscale_ok = rx_opt->snd_wscale = 0; | 260 | rx_opt->tstamp_ok = rx_opt->sack_ok = 0; |
261 | rx_opt->wscale_ok = rx_opt->snd_wscale = 0; | ||
262 | rx_opt->cookie_plus = 0; | ||
259 | } | 263 | } |
260 | 264 | ||
261 | /* This is the max number of SACKS that we'll generate and process. It's safe | 265 | /* This is the max number of SACKS that we'll generate and process. It's safe |
262 | * to increse this, although since: | 266 | * to increase this, although since: |
263 | * size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8) | 267 | * size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8) |
264 | * only four options will fit in a standard TCP header */ | 268 | * only four options will fit in a standard TCP header */ |
265 | #define TCP_NUM_SACKS 4 | 269 | #define TCP_NUM_SACKS 4 |
266 | 270 | ||
271 | struct tcp_cookie_values; | ||
272 | struct tcp_request_sock_ops; | ||
273 | |||
267 | struct tcp_request_sock { | 274 | struct tcp_request_sock { |
268 | struct inet_request_sock req; | 275 | struct inet_request_sock req; |
269 | #ifdef CONFIG_TCP_MD5SIG | 276 | #ifdef CONFIG_TCP_MD5SIG |
270 | /* Only used by TCP MD5 Signature so far. */ | 277 | /* Only used by TCP MD5 Signature so far. */ |
271 | const struct tcp_request_sock_ops *af_specific; | 278 | const struct tcp_request_sock_ops *af_specific; |
272 | #endif | 279 | #endif |
273 | u32 rcv_isn; | 280 | u32 rcv_isn; |
274 | u32 snt_isn; | 281 | u32 snt_isn; |
275 | }; | 282 | }; |
276 | 283 | ||
277 | static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) | 284 | static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) |
@@ -441,6 +448,12 @@ struct tcp_sock { | |||
441 | /* TCP MD5 Signature Option information */ | 448 | /* TCP MD5 Signature Option information */ |
442 | struct tcp_md5sig_info *md5sig_info; | 449 | struct tcp_md5sig_info *md5sig_info; |
443 | #endif | 450 | #endif |
451 | |||
452 | /* When the cookie options are generated and exchanged, then this | ||
453 | * object holds a reference to them (cookie_values->kref). Also | ||
454 | * contains related tcp_cookie_transactions fields. | ||
455 | */ | ||
456 | struct tcp_cookie_values *cookie_values; | ||
444 | }; | 457 | }; |
445 | 458 | ||
446 | static inline struct tcp_sock *tcp_sk(const struct sock *sk) | 459 | static inline struct tcp_sock *tcp_sk(const struct sock *sk) |
@@ -459,6 +472,10 @@ struct tcp_timewait_sock { | |||
459 | u16 tw_md5_keylen; | 472 | u16 tw_md5_keylen; |
460 | u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN]; | 473 | u8 tw_md5_key[TCP_MD5SIG_MAXKEYLEN]; |
461 | #endif | 474 | #endif |
475 | /* Few sockets in timewait have cookies; in that case, then this | ||
476 | * object holds a reference to them (tw_cookie_values->kref). | ||
477 | */ | ||
478 | struct tcp_cookie_values *tw_cookie_values; | ||
462 | }; | 479 | }; |
463 | 480 | ||
464 | static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) | 481 | static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) |
diff --git a/include/net/tcp.h b/include/net/tcp.h index 738b65f01e26..f9abd9becabd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/dmaengine.h> | 30 | #include <linux/dmaengine.h> |
31 | #include <linux/crypto.h> | 31 | #include <linux/crypto.h> |
32 | #include <linux/cryptohash.h> | 32 | #include <linux/cryptohash.h> |
33 | #include <linux/kref.h> | ||
33 | 34 | ||
34 | #include <net/inet_connection_sock.h> | 35 | #include <net/inet_connection_sock.h> |
35 | #include <net/inet_timewait_sock.h> | 36 | #include <net/inet_timewait_sock.h> |
@@ -164,6 +165,7 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); | |||
164 | #define TCPOPT_SACK 5 /* SACK Block */ | 165 | #define TCPOPT_SACK 5 /* SACK Block */ |
165 | #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ | 166 | #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ |
166 | #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ | 167 | #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ |
168 | #define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */ | ||
167 | 169 | ||
168 | /* | 170 | /* |
169 | * TCP option lengths | 171 | * TCP option lengths |
@@ -174,6 +176,10 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); | |||
174 | #define TCPOLEN_SACK_PERM 2 | 176 | #define TCPOLEN_SACK_PERM 2 |
175 | #define TCPOLEN_TIMESTAMP 10 | 177 | #define TCPOLEN_TIMESTAMP 10 |
176 | #define TCPOLEN_MD5SIG 18 | 178 | #define TCPOLEN_MD5SIG 18 |
179 | #define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */ | ||
180 | #define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */ | ||
181 | #define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MIN) | ||
182 | #define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MAX) | ||
177 | 183 | ||
178 | /* But this is what stacks really send out. */ | 184 | /* But this is what stacks really send out. */ |
179 | #define TCPOLEN_TSTAMP_ALIGNED 12 | 185 | #define TCPOLEN_TSTAMP_ALIGNED 12 |
@@ -1482,6 +1488,83 @@ struct tcp_request_sock_ops { | |||
1482 | 1488 | ||
1483 | extern int tcp_cookie_generator(u32 *bakery); | 1489 | extern int tcp_cookie_generator(u32 *bakery); |
1484 | 1490 | ||
1491 | /** | ||
1492 | * struct tcp_cookie_values - each socket needs extra space for the | ||
1493 | * cookies, together with (optional) space for any SYN data. | ||
1494 | * | ||
1495 | * A tcp_sock contains a pointer to the current value, and this is | ||
1496 | * cloned to the tcp_timewait_sock. | ||
1497 | * | ||
1498 | * @cookie_pair: variable data from the option exchange. | ||
1499 | * | ||
1500 | * @cookie_desired: user specified tcpct_cookie_desired. Zero | ||
1501 | * indicates default (sysctl_tcp_cookie_size). | ||
1502 | * After cookie sent, remembers size of cookie. | ||
1503 | * Range 0, TCP_COOKIE_MIN to TCP_COOKIE_MAX. | ||
1504 | * | ||
1505 | * @s_data_desired: user specified tcpct_s_data_desired. When the | ||
1506 | * constant payload is specified (@s_data_constant), | ||
1507 | * holds its length instead. | ||
1508 | * Range 0 to TCP_MSS_DESIRED. | ||
1509 | * | ||
1510 | * @s_data_payload: constant data that is to be included in the | ||
1511 | * payload of SYN or SYNACK segments when the | ||
1512 | * cookie option is present. | ||
1513 | */ | ||
1514 | struct tcp_cookie_values { | ||
1515 | struct kref kref; | ||
1516 | u8 cookie_pair[TCP_COOKIE_PAIR_SIZE]; | ||
1517 | u8 cookie_pair_size; | ||
1518 | u8 cookie_desired; | ||
1519 | u16 s_data_desired:11, | ||
1520 | s_data_constant:1, | ||
1521 | s_data_in:1, | ||
1522 | s_data_out:1, | ||
1523 | s_data_unused:2; | ||
1524 | u8 s_data_payload[0]; | ||
1525 | }; | ||
1526 | |||
1527 | static inline void tcp_cookie_values_release(struct kref *kref) | ||
1528 | { | ||
1529 | kfree(container_of(kref, struct tcp_cookie_values, kref)); | ||
1530 | } | ||
1531 | |||
1532 | /* The length of constant payload data. Note that s_data_desired is | ||
1533 | * overloaded, depending on s_data_constant: either the length of constant | ||
1534 | * data (returned here) or the limit on variable data. | ||
1535 | */ | ||
1536 | static inline int tcp_s_data_size(const struct tcp_sock *tp) | ||
1537 | { | ||
1538 | return (tp->cookie_values != NULL && tp->cookie_values->s_data_constant) | ||
1539 | ? tp->cookie_values->s_data_desired | ||
1540 | : 0; | ||
1541 | } | ||
1542 | |||
1543 | /** | ||
1544 | * struct tcp_extend_values - tcp_ipv?.c to tcp_output.c workspace. | ||
1545 | * | ||
1546 | * As tcp_request_sock has already been extended in other places, the | ||
1547 | * only remaining method is to pass stack values along as function | ||
1548 | * parameters. These parameters are not needed after sending SYNACK. | ||
1549 | * | ||
1550 | * @cookie_bakery: cryptographic secret and message workspace. | ||
1551 | * | ||
1552 | * @cookie_plus: bytes in authenticator/cookie option, copied from | ||
1553 | * struct tcp_options_received (above). | ||
1554 | */ | ||
1555 | struct tcp_extend_values { | ||
1556 | struct request_values rv; | ||
1557 | u32 cookie_bakery[COOKIE_WORKSPACE_WORDS]; | ||
1558 | u8 cookie_plus:6, | ||
1559 | cookie_out_never:1, | ||
1560 | cookie_in_always:1; | ||
1561 | }; | ||
1562 | |||
1563 | static inline struct tcp_extend_values *tcp_xv(struct request_values *rvp) | ||
1564 | { | ||
1565 | return (struct tcp_extend_values *)rvp; | ||
1566 | } | ||
1567 | |||
1485 | extern void tcp_v4_init(void); | 1568 | extern void tcp_v4_init(void); |
1486 | extern void tcp_init(void); | 1569 | extern void tcp_init(void); |
1487 | 1570 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 649a36d99c73..a2bcac9b388e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -1833,6 +1833,19 @@ static int tcp_v4_init_sock(struct sock *sk) | |||
1833 | tp->af_specific = &tcp_sock_ipv4_specific; | 1833 | tp->af_specific = &tcp_sock_ipv4_specific; |
1834 | #endif | 1834 | #endif |
1835 | 1835 | ||
1836 | /* TCP Cookie Transactions */ | ||
1837 | if (sysctl_tcp_cookie_size > 0) { | ||
1838 | /* Default, cookies without s_data_payload. */ | ||
1839 | tp->cookie_values = | ||
1840 | kzalloc(sizeof(*tp->cookie_values), | ||
1841 | sk->sk_allocation); | ||
1842 | if (tp->cookie_values != NULL) | ||
1843 | kref_init(&tp->cookie_values->kref); | ||
1844 | } | ||
1845 | /* Presumed zeroed, in order of appearance: | ||
1846 | * cookie_in_always, cookie_out_never, | ||
1847 | * s_data_constant, s_data_in, s_data_out | ||
1848 | */ | ||
1836 | sk->sk_sndbuf = sysctl_tcp_wmem[1]; | 1849 | sk->sk_sndbuf = sysctl_tcp_wmem[1]; |
1837 | sk->sk_rcvbuf = sysctl_tcp_rmem[1]; | 1850 | sk->sk_rcvbuf = sysctl_tcp_rmem[1]; |
1838 | 1851 | ||
@@ -1886,6 +1899,13 @@ void tcp_v4_destroy_sock(struct sock *sk) | |||
1886 | sk->sk_sndmsg_page = NULL; | 1899 | sk->sk_sndmsg_page = NULL; |
1887 | } | 1900 | } |
1888 | 1901 | ||
1902 | /* TCP Cookie Transactions */ | ||
1903 | if (tp->cookie_values != NULL) { | ||
1904 | kref_put(&tp->cookie_values->kref, | ||
1905 | tcp_cookie_values_release); | ||
1906 | tp->cookie_values = NULL; | ||
1907 | } | ||
1908 | |||
1889 | percpu_counter_dec(&tcp_sockets_allocated); | 1909 | percpu_counter_dec(&tcp_sockets_allocated); |
1890 | } | 1910 | } |
1891 | 1911 | ||
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index d3f6bbfc76f0..96852af43ca7 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c | |||
@@ -383,14 +383,43 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, | |||
383 | const struct inet_request_sock *ireq = inet_rsk(req); | 383 | const struct inet_request_sock *ireq = inet_rsk(req); |
384 | struct tcp_request_sock *treq = tcp_rsk(req); | 384 | struct tcp_request_sock *treq = tcp_rsk(req); |
385 | struct inet_connection_sock *newicsk = inet_csk(newsk); | 385 | struct inet_connection_sock *newicsk = inet_csk(newsk); |
386 | struct tcp_sock *newtp; | 386 | struct tcp_sock *newtp = tcp_sk(newsk); |
387 | struct tcp_sock *oldtp = tcp_sk(sk); | ||
388 | struct tcp_cookie_values *oldcvp = oldtp->cookie_values; | ||
389 | |||
390 | /* TCP Cookie Transactions require space for the cookie pair, | ||
391 | * as it differs for each connection. There is no need to | ||
392 | * copy any s_data_payload stored at the original socket. | ||
393 | * Failure will prevent resuming the connection. | ||
394 | * | ||
395 | * Presumed copied, in order of appearance: | ||
396 | * cookie_in_always, cookie_out_never | ||
397 | */ | ||
398 | if (oldcvp != NULL) { | ||
399 | struct tcp_cookie_values *newcvp = | ||
400 | kzalloc(sizeof(*newtp->cookie_values), | ||
401 | GFP_ATOMIC); | ||
402 | |||
403 | if (newcvp != NULL) { | ||
404 | kref_init(&newcvp->kref); | ||
405 | newcvp->cookie_desired = | ||
406 | oldcvp->cookie_desired; | ||
407 | newtp->cookie_values = newcvp; | ||
408 | } else { | ||
409 | /* Not Yet Implemented */ | ||
410 | newtp->cookie_values = NULL; | ||
411 | } | ||
412 | } | ||
387 | 413 | ||
388 | /* Now setup tcp_sock */ | 414 | /* Now setup tcp_sock */ |
389 | newtp = tcp_sk(newsk); | ||
390 | newtp->pred_flags = 0; | 415 | newtp->pred_flags = 0; |
391 | newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; | 416 | |
392 | newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = treq->snt_isn + 1; | 417 | newtp->rcv_wup = newtp->copied_seq = |
393 | newtp->snd_up = treq->snt_isn + 1; | 418 | newtp->rcv_nxt = treq->rcv_isn + 1; |
419 | |||
420 | newtp->snd_sml = newtp->snd_una = | ||
421 | newtp->snd_nxt = newtp->snd_up = | ||
422 | treq->snt_isn + 1 + tcp_s_data_size(oldtp); | ||
394 | 423 | ||
395 | tcp_prequeue_init(newtp); | 424 | tcp_prequeue_init(newtp); |
396 | 425 | ||
@@ -423,8 +452,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, | |||
423 | tcp_set_ca_state(newsk, TCP_CA_Open); | 452 | tcp_set_ca_state(newsk, TCP_CA_Open); |
424 | tcp_init_xmit_timers(newsk); | 453 | tcp_init_xmit_timers(newsk); |
425 | skb_queue_head_init(&newtp->out_of_order_queue); | 454 | skb_queue_head_init(&newtp->out_of_order_queue); |
426 | newtp->write_seq = treq->snt_isn + 1; | 455 | newtp->write_seq = newtp->pushed_seq = |
427 | newtp->pushed_seq = newtp->write_seq; | 456 | treq->snt_isn + 1 + tcp_s_data_size(oldtp); |
428 | 457 | ||
429 | newtp->rx_opt.saw_tstamp = 0; | 458 | newtp->rx_opt.saw_tstamp = 0; |
430 | 459 | ||
@@ -590,7 +619,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, | |||
590 | * Invalid ACK: reset will be sent by listening socket | 619 | * Invalid ACK: reset will be sent by listening socket |
591 | */ | 620 | */ |
592 | if ((flg & TCP_FLAG_ACK) && | 621 | if ((flg & TCP_FLAG_ACK) && |
593 | (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1)) | 622 | (TCP_SKB_CB(skb)->ack_seq != |
623 | tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk)))) | ||
594 | return sk; | 624 | return sk; |
595 | 625 | ||
596 | /* Also, it would be not so bad idea to check rcv_tsecr, which | 626 | /* Also, it would be not so bad idea to check rcv_tsecr, which |
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index da6e24416d75..f2ec38289a4a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -1864,6 +1864,19 @@ static int tcp_v6_init_sock(struct sock *sk) | |||
1864 | tp->af_specific = &tcp_sock_ipv6_specific; | 1864 | tp->af_specific = &tcp_sock_ipv6_specific; |
1865 | #endif | 1865 | #endif |
1866 | 1866 | ||
1867 | /* TCP Cookie Transactions */ | ||
1868 | if (sysctl_tcp_cookie_size > 0) { | ||
1869 | /* Default, cookies without s_data_payload. */ | ||
1870 | tp->cookie_values = | ||
1871 | kzalloc(sizeof(*tp->cookie_values), | ||
1872 | sk->sk_allocation); | ||
1873 | if (tp->cookie_values != NULL) | ||
1874 | kref_init(&tp->cookie_values->kref); | ||
1875 | } | ||
1876 | /* Presumed zeroed, in order of appearance: | ||
1877 | * cookie_in_always, cookie_out_never, | ||
1878 | * s_data_constant, s_data_in, s_data_out | ||
1879 | */ | ||
1867 | sk->sk_sndbuf = sysctl_tcp_wmem[1]; | 1880 | sk->sk_sndbuf = sysctl_tcp_wmem[1]; |
1868 | sk->sk_rcvbuf = sysctl_tcp_rmem[1]; | 1881 | sk->sk_rcvbuf = sysctl_tcp_rmem[1]; |
1869 | 1882 | ||