diff options
Diffstat (limited to 'net/dccp/timer.c')
-rw-r--r-- | net/dccp/timer.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/net/dccp/timer.c b/net/dccp/timer.c new file mode 100644 index 000000000000..aa34b576e228 --- /dev/null +++ b/net/dccp/timer.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * net/dccp/timer.c | ||
3 | * | ||
4 | * An implementation of the DCCP protocol | ||
5 | * Arnaldo Carvalho de Melo <acme@conectiva.com.br> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/dccp.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | |||
17 | #include "dccp.h" | ||
18 | |||
19 | static void dccp_write_timer(unsigned long data); | ||
20 | static void dccp_keepalive_timer(unsigned long data); | ||
21 | static void dccp_delack_timer(unsigned long data); | ||
22 | |||
23 | void dccp_init_xmit_timers(struct sock *sk) | ||
24 | { | ||
25 | inet_csk_init_xmit_timers(sk, &dccp_write_timer, &dccp_delack_timer, | ||
26 | &dccp_keepalive_timer); | ||
27 | } | ||
28 | |||
29 | static void dccp_write_err(struct sock *sk) | ||
30 | { | ||
31 | sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT; | ||
32 | sk->sk_error_report(sk); | ||
33 | |||
34 | dccp_v4_send_reset(sk, DCCP_RESET_CODE_ABORTED); | ||
35 | dccp_done(sk); | ||
36 | DCCP_INC_STATS_BH(DCCP_MIB_ABORTONTIMEOUT); | ||
37 | } | ||
38 | |||
39 | /* A write timeout has occurred. Process the after effects. */ | ||
40 | static int dccp_write_timeout(struct sock *sk) | ||
41 | { | ||
42 | const struct inet_connection_sock *icsk = inet_csk(sk); | ||
43 | int retry_until; | ||
44 | |||
45 | if (sk->sk_state == DCCP_REQUESTING || sk->sk_state == DCCP_PARTOPEN) { | ||
46 | if (icsk->icsk_retransmits != 0) | ||
47 | dst_negative_advice(&sk->sk_dst_cache); | ||
48 | retry_until = icsk->icsk_syn_retries ? : | ||
49 | /* FIXME! */ 3 /* FIXME! sysctl_tcp_syn_retries */; | ||
50 | } else { | ||
51 | if (icsk->icsk_retransmits >= | ||
52 | /* FIXME! sysctl_tcp_retries1 */ 5 /* FIXME! */) { | ||
53 | /* NOTE. draft-ietf-tcpimpl-pmtud-01.txt requires pmtu | ||
54 | black hole detection. :-( | ||
55 | |||
56 | It is place to make it. It is not made. I do not want | ||
57 | to make it. It is disguisting. It does not work in any | ||
58 | case. Let me to cite the same draft, which requires for | ||
59 | us to implement this: | ||
60 | |||
61 | "The one security concern raised by this memo is that ICMP black holes | ||
62 | are often caused by over-zealous security administrators who block | ||
63 | all ICMP messages. It is vitally important that those who design and | ||
64 | deploy security systems understand the impact of strict filtering on | ||
65 | upper-layer protocols. The safest web site in the world is worthless | ||
66 | if most TCP implementations cannot transfer data from it. It would | ||
67 | be far nicer to have all of the black holes fixed rather than fixing | ||
68 | all of the TCP implementations." | ||
69 | |||
70 | Golden words :-). | ||
71 | */ | ||
72 | |||
73 | dst_negative_advice(&sk->sk_dst_cache); | ||
74 | } | ||
75 | |||
76 | retry_until = /* FIXME! */ 15 /* FIXME! sysctl_tcp_retries2 */; | ||
77 | /* | ||
78 | * FIXME: see tcp_write_timout and tcp_out_of_resources | ||
79 | */ | ||
80 | } | ||
81 | |||
82 | if (icsk->icsk_retransmits >= retry_until) { | ||
83 | /* Has it gone just too far? */ | ||
84 | dccp_write_err(sk); | ||
85 | return 1; | ||
86 | } | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /* This is the same as tcp_delack_timer, sans prequeue & mem_reclaim stuff */ | ||
91 | static void dccp_delack_timer(unsigned long data) | ||
92 | { | ||
93 | struct sock *sk = (struct sock *)data; | ||
94 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
95 | |||
96 | bh_lock_sock(sk); | ||
97 | if (sock_owned_by_user(sk)) { | ||
98 | /* Try again later. */ | ||
99 | icsk->icsk_ack.blocked = 1; | ||
100 | NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKLOCKED); | ||
101 | sk_reset_timer(sk, &icsk->icsk_delack_timer, | ||
102 | jiffies + TCP_DELACK_MIN); | ||
103 | goto out; | ||
104 | } | ||
105 | |||
106 | if (sk->sk_state == DCCP_CLOSED || | ||
107 | !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) | ||
108 | goto out; | ||
109 | if (time_after(icsk->icsk_ack.timeout, jiffies)) { | ||
110 | sk_reset_timer(sk, &icsk->icsk_delack_timer, | ||
111 | icsk->icsk_ack.timeout); | ||
112 | goto out; | ||
113 | } | ||
114 | |||
115 | icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER; | ||
116 | |||
117 | if (inet_csk_ack_scheduled(sk)) { | ||
118 | if (!icsk->icsk_ack.pingpong) { | ||
119 | /* Delayed ACK missed: inflate ATO. */ | ||
120 | icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, | ||
121 | icsk->icsk_rto); | ||
122 | } else { | ||
123 | /* Delayed ACK missed: leave pingpong mode and | ||
124 | * deflate ATO. | ||
125 | */ | ||
126 | icsk->icsk_ack.pingpong = 0; | ||
127 | icsk->icsk_ack.ato = TCP_ATO_MIN; | ||
128 | } | ||
129 | dccp_send_ack(sk); | ||
130 | NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKS); | ||
131 | } | ||
132 | out: | ||
133 | bh_unlock_sock(sk); | ||
134 | sock_put(sk); | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * The DCCP retransmit timer. | ||
139 | */ | ||
140 | static void dccp_retransmit_timer(struct sock *sk) | ||
141 | { | ||
142 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
143 | |||
144 | /* | ||
145 | * sk->sk_send_head has to have one skb with | ||
146 | * DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP | ||
147 | * packet types (REQUEST, RESPONSE, the ACK in the 3way handshake | ||
148 | * (PARTOPEN timer), etc). | ||
149 | */ | ||
150 | BUG_TRAP(sk->sk_send_head != NULL); | ||
151 | |||
152 | /* | ||
153 | * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was | ||
154 | * sent, no need to retransmit, this sock is dead. | ||
155 | */ | ||
156 | if (dccp_write_timeout(sk)) | ||
157 | goto out; | ||
158 | |||
159 | /* | ||
160 | * We want to know the number of packets retransmitted, not the | ||
161 | * total number of retransmissions of clones of original packets. | ||
162 | */ | ||
163 | if (icsk->icsk_retransmits == 0) | ||
164 | DCCP_INC_STATS_BH(DCCP_MIB_TIMEOUTS); | ||
165 | |||
166 | if (dccp_retransmit_skb(sk, sk->sk_send_head) < 0) { | ||
167 | /* | ||
168 | * Retransmission failed because of local congestion, | ||
169 | * do not backoff. | ||
170 | */ | ||
171 | if (icsk->icsk_retransmits == 0) | ||
172 | icsk->icsk_retransmits = 1; | ||
173 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, | ||
174 | min(icsk->icsk_rto, | ||
175 | TCP_RESOURCE_PROBE_INTERVAL), | ||
176 | DCCP_RTO_MAX); | ||
177 | goto out; | ||
178 | } | ||
179 | |||
180 | icsk->icsk_backoff++; | ||
181 | icsk->icsk_retransmits++; | ||
182 | |||
183 | icsk->icsk_rto = min(icsk->icsk_rto << 1, DCCP_RTO_MAX); | ||
184 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, | ||
185 | DCCP_RTO_MAX); | ||
186 | if (icsk->icsk_retransmits > 3 /* FIXME: sysctl_dccp_retries1 */) | ||
187 | __sk_dst_reset(sk); | ||
188 | out:; | ||
189 | } | ||
190 | |||
191 | static void dccp_write_timer(unsigned long data) | ||
192 | { | ||
193 | struct sock *sk = (struct sock *)data; | ||
194 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
195 | int event = 0; | ||
196 | |||
197 | bh_lock_sock(sk); | ||
198 | if (sock_owned_by_user(sk)) { | ||
199 | /* Try again later */ | ||
200 | sk_reset_timer(sk, &icsk->icsk_retransmit_timer, | ||
201 | jiffies + (HZ / 20)); | ||
202 | goto out; | ||
203 | } | ||
204 | |||
205 | if (sk->sk_state == DCCP_CLOSED || !icsk->icsk_pending) | ||
206 | goto out; | ||
207 | |||
208 | if (time_after(icsk->icsk_timeout, jiffies)) { | ||
209 | sk_reset_timer(sk, &icsk->icsk_retransmit_timer, | ||
210 | icsk->icsk_timeout); | ||
211 | goto out; | ||
212 | } | ||
213 | |||
214 | event = icsk->icsk_pending; | ||
215 | icsk->icsk_pending = 0; | ||
216 | |||
217 | switch (event) { | ||
218 | case ICSK_TIME_RETRANS: | ||
219 | dccp_retransmit_timer(sk); | ||
220 | break; | ||
221 | } | ||
222 | out: | ||
223 | bh_unlock_sock(sk); | ||
224 | sock_put(sk); | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * Timer for listening sockets | ||
229 | */ | ||
230 | static void dccp_response_timer(struct sock *sk) | ||
231 | { | ||
232 | inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, DCCP_TIMEOUT_INIT, | ||
233 | DCCP_RTO_MAX); | ||
234 | } | ||
235 | |||
236 | static void dccp_keepalive_timer(unsigned long data) | ||
237 | { | ||
238 | struct sock *sk = (struct sock *)data; | ||
239 | |||
240 | /* Only process if socket is not in use. */ | ||
241 | bh_lock_sock(sk); | ||
242 | if (sock_owned_by_user(sk)) { | ||
243 | /* Try again later. */ | ||
244 | inet_csk_reset_keepalive_timer(sk, HZ / 20); | ||
245 | goto out; | ||
246 | } | ||
247 | |||
248 | if (sk->sk_state == DCCP_LISTEN) { | ||
249 | dccp_response_timer(sk); | ||
250 | goto out; | ||
251 | } | ||
252 | out: | ||
253 | bh_unlock_sock(sk); | ||
254 | sock_put(sk); | ||
255 | } | ||