diff options
author | Arnaldo Carvalho de Melo <acme@ghostprotocols.net> | 2005-08-09 23:14:34 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 18:49:46 -0400 |
commit | 7c657876b63cb1d8a2ec06f8fc6c37bb8412e66c (patch) | |
tree | 3cb2732870c9cf8f976cb6fa57e0223f1c648e2a /net/dccp/timer.c | |
parent | c4365c9235f80128c3c3d5993074173941b1c1f0 (diff) |
[DCCP]: Initial implementation
Development to this point was done on a subversion repository at:
http://oops.ghostprotocols.net:81/cgi-bin/viewcvs.cgi/dccp-2.6/
This repository will be kept at this site for the foreseable future,
so that interested parties can see the history of this code,
attributions, etc.
If I ever decide to take this offline I'll provide the full history at
some other suitable place.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dccp/timer.c')
-rw-r--r-- | net/dccp/timer.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/net/dccp/timer.c b/net/dccp/timer.c new file mode 100644 index 000000000000..8c396ee01aac --- /dev/null +++ b/net/dccp/timer.c | |||
@@ -0,0 +1,249 @@ | |||
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 ? : /* FIXME! */ 3 /* FIXME! sysctl_tcp_syn_retries */; | ||
49 | } else { | ||
50 | if (icsk->icsk_retransmits >= /* FIXME! sysctl_tcp_retries1 */ 5 /* FIXME! */) { | ||
51 | /* NOTE. draft-ietf-tcpimpl-pmtud-01.txt requires pmtu black | ||
52 | hole detection. :-( | ||
53 | |||
54 | It is place to make it. It is not made. I do not want | ||
55 | to make it. It is disguisting. It does not work in any | ||
56 | case. Let me to cite the same draft, which requires for | ||
57 | us to implement this: | ||
58 | |||
59 | "The one security concern raised by this memo is that ICMP black holes | ||
60 | are often caused by over-zealous security administrators who block | ||
61 | all ICMP messages. It is vitally important that those who design and | ||
62 | deploy security systems understand the impact of strict filtering on | ||
63 | upper-layer protocols. The safest web site in the world is worthless | ||
64 | if most TCP implementations cannot transfer data from it. It would | ||
65 | be far nicer to have all of the black holes fixed rather than fixing | ||
66 | all of the TCP implementations." | ||
67 | |||
68 | Golden words :-). | ||
69 | */ | ||
70 | |||
71 | dst_negative_advice(&sk->sk_dst_cache); | ||
72 | } | ||
73 | |||
74 | retry_until = /* FIXME! */ 15 /* FIXME! sysctl_tcp_retries2 */; | ||
75 | /* | ||
76 | * FIXME: see tcp_write_timout and tcp_out_of_resources | ||
77 | */ | ||
78 | } | ||
79 | |||
80 | if (icsk->icsk_retransmits >= retry_until) { | ||
81 | /* Has it gone just too far? */ | ||
82 | dccp_write_err(sk); | ||
83 | return 1; | ||
84 | } | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | /* This is the same as tcp_delack_timer, sans prequeue & mem_reclaim stuff */ | ||
89 | static void dccp_delack_timer(unsigned long data) | ||
90 | { | ||
91 | struct sock *sk = (struct sock *)data; | ||
92 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
93 | |||
94 | bh_lock_sock(sk); | ||
95 | if (sock_owned_by_user(sk)) { | ||
96 | /* Try again later. */ | ||
97 | icsk->icsk_ack.blocked = 1; | ||
98 | NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKLOCKED); | ||
99 | sk_reset_timer(sk, &icsk->icsk_delack_timer, jiffies + TCP_DELACK_MIN); | ||
100 | goto out; | ||
101 | } | ||
102 | |||
103 | if (sk->sk_state == DCCP_CLOSED || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER)) | ||
104 | goto out; | ||
105 | if (time_after(icsk->icsk_ack.timeout, jiffies)) { | ||
106 | sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout); | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER; | ||
111 | |||
112 | if (inet_csk_ack_scheduled(sk)) { | ||
113 | if (!icsk->icsk_ack.pingpong) { | ||
114 | /* Delayed ACK missed: inflate ATO. */ | ||
115 | icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto); | ||
116 | } else { | ||
117 | /* Delayed ACK missed: leave pingpong mode and | ||
118 | * deflate ATO. | ||
119 | */ | ||
120 | icsk->icsk_ack.pingpong = 0; | ||
121 | icsk->icsk_ack.ato = TCP_ATO_MIN; | ||
122 | } | ||
123 | dccp_send_ack(sk); | ||
124 | NET_INC_STATS_BH(LINUX_MIB_DELAYEDACKS); | ||
125 | } | ||
126 | out: | ||
127 | bh_unlock_sock(sk); | ||
128 | sock_put(sk); | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * The DCCP retransmit timer. | ||
133 | */ | ||
134 | static void dccp_retransmit_timer(struct sock *sk) | ||
135 | { | ||
136 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
137 | |||
138 | /* | ||
139 | * sk->sk_send_head has to have one skb with | ||
140 | * DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP | ||
141 | * packet types (REQUEST, RESPONSE, the ACK in the 3way hanshake | ||
142 | * (PARTOPEN timer), etc). | ||
143 | */ | ||
144 | BUG_TRAP(sk->sk_send_head != NULL); | ||
145 | |||
146 | /* | ||
147 | * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was | ||
148 | * sent, no need to retransmit, this sock is dead. | ||
149 | */ | ||
150 | if (dccp_write_timeout(sk)) | ||
151 | goto out; | ||
152 | |||
153 | /* | ||
154 | * We want to know the number of packets retransmitted, not the | ||
155 | * total number of retransmissions of clones of original packets. | ||
156 | */ | ||
157 | if (icsk->icsk_retransmits == 0) | ||
158 | DCCP_INC_STATS_BH(DCCP_MIB_TIMEOUTS); | ||
159 | |||
160 | if (dccp_retransmit_skb(sk, sk->sk_send_head) < 0) { | ||
161 | /* | ||
162 | * Retransmission failed because of local congestion, | ||
163 | * do not backoff. | ||
164 | */ | ||
165 | if (icsk->icsk_retransmits == 0) | ||
166 | icsk->icsk_retransmits = 1; | ||
167 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, | ||
168 | min(icsk->icsk_rto, | ||
169 | TCP_RESOURCE_PROBE_INTERVAL), | ||
170 | TCP_RTO_MAX); | ||
171 | goto out; | ||
172 | } | ||
173 | |||
174 | icsk->icsk_backoff++; | ||
175 | icsk->icsk_retransmits++; | ||
176 | |||
177 | icsk->icsk_rto = min(icsk->icsk_rto << 1, DCCP_RTO_MAX); | ||
178 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); | ||
179 | if (icsk->icsk_retransmits > 3 /* FIXME: sysctl_dccp_retries1 */) | ||
180 | __sk_dst_reset(sk); | ||
181 | out:; | ||
182 | } | ||
183 | |||
184 | static void dccp_write_timer(unsigned long data) | ||
185 | { | ||
186 | struct sock *sk = (struct sock *)data; | ||
187 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
188 | int event = 0; | ||
189 | |||
190 | bh_lock_sock(sk); | ||
191 | if (sock_owned_by_user(sk)) { | ||
192 | /* Try again later */ | ||
193 | sk_reset_timer(sk, &icsk->icsk_retransmit_timer, jiffies + (HZ / 20)); | ||
194 | goto out; | ||
195 | } | ||
196 | |||
197 | if (sk->sk_state == DCCP_CLOSED || !icsk->icsk_pending) | ||
198 | goto out; | ||
199 | |||
200 | if (time_after(icsk->icsk_timeout, jiffies)) { | ||
201 | sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout); | ||
202 | goto out; | ||
203 | } | ||
204 | |||
205 | event = icsk->icsk_pending; | ||
206 | icsk->icsk_pending = 0; | ||
207 | |||
208 | switch (event) { | ||
209 | case ICSK_TIME_RETRANS: | ||
210 | dccp_retransmit_timer(sk); | ||
211 | break; | ||
212 | } | ||
213 | out: | ||
214 | bh_unlock_sock(sk); | ||
215 | sock_put(sk); | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Timer for listening sockets | ||
220 | */ | ||
221 | static void dccp_response_timer(struct sock *sk) | ||
222 | { | ||
223 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
224 | const int max_retries = icsk->icsk_syn_retries ? : TCP_SYNACK_RETRIES /* FIXME sysctl_tcp_synack_retries */; | ||
225 | |||
226 | reqsk_queue_prune(&icsk->icsk_accept_queue, sk, TCP_SYNQ_INTERVAL, | ||
227 | DCCP_TIMEOUT_INIT, DCCP_RTO_MAX, max_retries); | ||
228 | } | ||
229 | |||
230 | static void dccp_keepalive_timer(unsigned long data) | ||
231 | { | ||
232 | struct sock *sk = (struct sock *)data; | ||
233 | |||
234 | /* Only process if socket is not in use. */ | ||
235 | bh_lock_sock(sk); | ||
236 | if (sock_owned_by_user(sk)) { | ||
237 | /* Try again later. */ | ||
238 | inet_csk_reset_keepalive_timer(sk, HZ / 20); | ||
239 | goto out; | ||
240 | } | ||
241 | |||
242 | if (sk->sk_state == DCCP_LISTEN) { | ||
243 | dccp_response_timer(sk); | ||
244 | goto out; | ||
245 | } | ||
246 | out: | ||
247 | bh_unlock_sock(sk); | ||
248 | sock_put(sk); | ||
249 | } | ||