diff options
Diffstat (limited to 'net/dccp')
-rw-r--r-- | net/dccp/Makefile | 2 | ||||
-rw-r--r-- | net/dccp/feat.c | 554 | ||||
-rw-r--r-- | net/dccp/feat.h | 28 | ||||
-rw-r--r-- | net/dccp/input.c | 3 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 19 | ||||
-rw-r--r-- | net/dccp/minisocks.c | 4 | ||||
-rw-r--r-- | net/dccp/options.c | 139 | ||||
-rw-r--r-- | net/dccp/output.c | 4 | ||||
-rw-r--r-- | net/dccp/proto.c | 53 | ||||
-rw-r--r-- | net/dccp/timer.c | 12 |
10 files changed, 813 insertions, 5 deletions
diff --git a/net/dccp/Makefile b/net/dccp/Makefile index 87b27fff6e3b..5736acea1c86 100644 --- a/net/dccp/Makefile +++ b/net/dccp/Makefile | |||
@@ -4,7 +4,7 @@ dccp_ipv6-y := ipv6.o | |||
4 | 4 | ||
5 | obj-$(CONFIG_IP_DCCP) += dccp.o | 5 | obj-$(CONFIG_IP_DCCP) += dccp.o |
6 | 6 | ||
7 | dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \ | 7 | dccp-y := ccid.o feat.o input.o ipv4.o minisocks.o options.o output.o proto.o \ |
8 | timer.o | 8 | timer.o |
9 | 9 | ||
10 | dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o | 10 | dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o |
diff --git a/net/dccp/feat.c b/net/dccp/feat.c new file mode 100644 index 000000000000..99d7b7f9efa9 --- /dev/null +++ b/net/dccp/feat.c | |||
@@ -0,0 +1,554 @@ | |||
1 | /* | ||
2 | * net/dccp/feat.c | ||
3 | * | ||
4 | * An implementation of the DCCP protocol | ||
5 | * Andrea Bittau <a.bittau@cs.ucl.ac.uk> | ||
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/module.h> | ||
15 | |||
16 | #include "dccp.h" | ||
17 | #include "feat.h" | ||
18 | |||
19 | #define DCCP_FEAT_SP_NOAGREE (-123) | ||
20 | |||
21 | int dccp_feat_change(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len, | ||
22 | gfp_t gfp) | ||
23 | { | ||
24 | struct dccp_sock *dp = dccp_sk(sk); | ||
25 | struct dccp_opt_pend *opt; | ||
26 | |||
27 | dccp_pr_debug("feat change type=%d feat=%d\n", type, feature); | ||
28 | |||
29 | /* check if that feature is already being negotiated */ | ||
30 | list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, | ||
31 | dccpop_node) { | ||
32 | /* ok we found a negotiation for this option already */ | ||
33 | if (opt->dccpop_feat == feature && opt->dccpop_type == type) { | ||
34 | dccp_pr_debug("Replacing old\n"); | ||
35 | /* replace */ | ||
36 | BUG_ON(opt->dccpop_val == NULL); | ||
37 | kfree(opt->dccpop_val); | ||
38 | opt->dccpop_val = val; | ||
39 | opt->dccpop_len = len; | ||
40 | opt->dccpop_conf = 0; | ||
41 | return 0; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | /* negotiation for a new feature */ | ||
46 | opt = kmalloc(sizeof(*opt), gfp); | ||
47 | if (opt == NULL) | ||
48 | return -ENOMEM; | ||
49 | |||
50 | opt->dccpop_type = type; | ||
51 | opt->dccpop_feat = feature; | ||
52 | opt->dccpop_len = len; | ||
53 | opt->dccpop_val = val; | ||
54 | opt->dccpop_conf = 0; | ||
55 | opt->dccpop_sc = NULL; | ||
56 | |||
57 | BUG_ON(opt->dccpop_val == NULL); | ||
58 | |||
59 | list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_pending); | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | EXPORT_SYMBOL_GPL(dccp_feat_change); | ||
64 | |||
65 | /* XXX taking only u8 vals */ | ||
66 | static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) | ||
67 | { | ||
68 | /* FIXME implement */ | ||
69 | dccp_pr_debug("changing [%d] feat %d to %d\n", type, feat, val); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, | ||
74 | u8 *rpref, u8 rlen) | ||
75 | { | ||
76 | struct dccp_sock *dp = dccp_sk(sk); | ||
77 | u8 *spref, slen, *res = NULL; | ||
78 | int i, j, rc, agree = 1; | ||
79 | |||
80 | BUG_ON(rpref == NULL); | ||
81 | |||
82 | /* check if we are the black sheep */ | ||
83 | if (dp->dccps_role == DCCP_ROLE_CLIENT) { | ||
84 | spref = rpref; | ||
85 | slen = rlen; | ||
86 | rpref = opt->dccpop_val; | ||
87 | rlen = opt->dccpop_len; | ||
88 | } else { | ||
89 | spref = opt->dccpop_val; | ||
90 | slen = opt->dccpop_len; | ||
91 | } | ||
92 | /* | ||
93 | * Now we have server preference list in spref and client preference in | ||
94 | * rpref | ||
95 | */ | ||
96 | BUG_ON(spref == NULL); | ||
97 | BUG_ON(rpref == NULL); | ||
98 | |||
99 | /* FIXME sanity check vals */ | ||
100 | |||
101 | /* Are values in any order? XXX Lame "algorithm" here */ | ||
102 | /* XXX assume values are 1 byte */ | ||
103 | for (i = 0; i < slen; i++) { | ||
104 | for (j = 0; j < rlen; j++) { | ||
105 | if (spref[i] == rpref[j]) { | ||
106 | res = &spref[i]; | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | if (res) | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | /* we didn't agree on anything */ | ||
115 | if (res == NULL) { | ||
116 | /* confirm previous value */ | ||
117 | switch (opt->dccpop_feat) { | ||
118 | case DCCPF_CCID: | ||
119 | /* XXX did i get this right? =P */ | ||
120 | if (opt->dccpop_type == DCCPO_CHANGE_L) | ||
121 | res = &dp->dccps_options.dccpo_tx_ccid; | ||
122 | else | ||
123 | res = &dp->dccps_options.dccpo_rx_ccid; | ||
124 | break; | ||
125 | |||
126 | default: | ||
127 | WARN_ON(1); /* XXX implement res */ | ||
128 | return -EFAULT; | ||
129 | } | ||
130 | |||
131 | dccp_pr_debug("Don't agree... reconfirming %d\n", *res); | ||
132 | agree = 0; /* this is used for mandatory options... */ | ||
133 | } | ||
134 | |||
135 | /* need to put result and our preference list */ | ||
136 | /* XXX assume 1 byte vals */ | ||
137 | rlen = 1 + opt->dccpop_len; | ||
138 | rpref = kmalloc(rlen, GFP_ATOMIC); | ||
139 | if (rpref == NULL) | ||
140 | return -ENOMEM; | ||
141 | |||
142 | *rpref = *res; | ||
143 | memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len); | ||
144 | |||
145 | /* put it in the "confirm queue" */ | ||
146 | if (opt->dccpop_sc == NULL) { | ||
147 | opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC); | ||
148 | if (opt->dccpop_sc == NULL) { | ||
149 | kfree(rpref); | ||
150 | return -ENOMEM; | ||
151 | } | ||
152 | } else { | ||
153 | /* recycle the confirm slot */ | ||
154 | BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); | ||
155 | kfree(opt->dccpop_sc->dccpoc_val); | ||
156 | dccp_pr_debug("recycling confirm slot\n"); | ||
157 | } | ||
158 | memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc)); | ||
159 | |||
160 | opt->dccpop_sc->dccpoc_val = rpref; | ||
161 | opt->dccpop_sc->dccpoc_len = rlen; | ||
162 | |||
163 | /* update the option on our side [we are about to send the confirm] */ | ||
164 | rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res); | ||
165 | if (rc) { | ||
166 | kfree(opt->dccpop_sc->dccpoc_val); | ||
167 | kfree(opt->dccpop_sc); | ||
168 | opt->dccpop_sc = 0; | ||
169 | return rc; | ||
170 | } | ||
171 | |||
172 | dccp_pr_debug("Will confirm %d\n", *rpref); | ||
173 | |||
174 | /* say we want to change to X but we just got a confirm X, suppress our | ||
175 | * change | ||
176 | */ | ||
177 | if (!opt->dccpop_conf) { | ||
178 | if (*opt->dccpop_val == *res) | ||
179 | opt->dccpop_conf = 1; | ||
180 | dccp_pr_debug("won't ask for change of same feature\n"); | ||
181 | } | ||
182 | |||
183 | return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */ | ||
184 | } | ||
185 | |||
186 | static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
187 | { | ||
188 | struct dccp_sock *dp = dccp_sk(sk); | ||
189 | struct dccp_opt_pend *opt; | ||
190 | int rc = 1; | ||
191 | u8 t; | ||
192 | |||
193 | /* | ||
194 | * We received a CHANGE. We gotta match it against our own preference | ||
195 | * list. If we got a CHANGE_R it means it's a change for us, so we need | ||
196 | * to compare our CHANGE_L list. | ||
197 | */ | ||
198 | if (type == DCCPO_CHANGE_L) | ||
199 | t = DCCPO_CHANGE_R; | ||
200 | else | ||
201 | t = DCCPO_CHANGE_L; | ||
202 | |||
203 | /* find our preference list for this feature */ | ||
204 | list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, | ||
205 | dccpop_node) { | ||
206 | if (opt->dccpop_type != t || opt->dccpop_feat != feature) | ||
207 | continue; | ||
208 | |||
209 | /* find the winner from the two preference lists */ | ||
210 | rc = dccp_feat_reconcile(sk, opt, val, len); | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | /* We didn't deal with the change. This can happen if we have no | ||
215 | * preference list for the feature. In fact, it just shouldn't | ||
216 | * happen---if we understand a feature, we should have a preference list | ||
217 | * with at least the default value. | ||
218 | */ | ||
219 | BUG_ON(rc == 1); | ||
220 | |||
221 | return rc; | ||
222 | } | ||
223 | |||
224 | static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
225 | { | ||
226 | struct dccp_opt_pend *opt; | ||
227 | struct dccp_sock *dp = dccp_sk(sk); | ||
228 | u8 *copy; | ||
229 | int rc; | ||
230 | |||
231 | /* NN features must be change L */ | ||
232 | if (type == DCCPO_CHANGE_R) { | ||
233 | dccp_pr_debug("received CHANGE_R %d for NN feat %d\n", | ||
234 | type, feature); | ||
235 | return -EFAULT; | ||
236 | } | ||
237 | |||
238 | /* XXX sanity check opt val */ | ||
239 | |||
240 | /* copy option so we can confirm it */ | ||
241 | opt = kzalloc(sizeof(*opt), GFP_ATOMIC); | ||
242 | if (opt == NULL) | ||
243 | return -ENOMEM; | ||
244 | |||
245 | copy = kmalloc(len, GFP_ATOMIC); | ||
246 | if (copy == NULL) { | ||
247 | kfree(opt); | ||
248 | return -ENOMEM; | ||
249 | } | ||
250 | memcpy(copy, val, len); | ||
251 | |||
252 | opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ | ||
253 | opt->dccpop_feat = feature; | ||
254 | opt->dccpop_val = copy; | ||
255 | opt->dccpop_len = len; | ||
256 | |||
257 | /* change feature */ | ||
258 | rc = dccp_feat_update(sk, type, feature, *val); | ||
259 | if (rc) { | ||
260 | kfree(opt->dccpop_val); | ||
261 | kfree(opt); | ||
262 | return rc; | ||
263 | } | ||
264 | |||
265 | dccp_pr_debug("Confirming NN feature %d (val=%d)\n", feature, *copy); | ||
266 | list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_conf); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static void dccp_feat_empty_confirm(struct sock *sk, u8 type, u8 feature) | ||
272 | { | ||
273 | struct dccp_sock *dp = dccp_sk(sk); | ||
274 | /* XXX check if other confirms for that are queued and recycle slot */ | ||
275 | struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC); | ||
276 | |||
277 | if (opt == NULL) { | ||
278 | /* XXX what do we do? Ignoring should be fine. It's a change | ||
279 | * after all =P | ||
280 | */ | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | opt->dccpop_type = type == DCCPO_CHANGE_L ? DCCPO_CONFIRM_R : | ||
285 | DCCPO_CONFIRM_L; | ||
286 | opt->dccpop_feat = feature; | ||
287 | opt->dccpop_val = 0; | ||
288 | opt->dccpop_len = 0; | ||
289 | |||
290 | /* change feature */ | ||
291 | dccp_pr_debug("Empty confirm feature %d type %d\n", feature, type); | ||
292 | list_add_tail(&opt->dccpop_node, &dp->dccps_options.dccpo_conf); | ||
293 | } | ||
294 | |||
295 | static void dccp_feat_flush_confirm(struct sock *sk) | ||
296 | { | ||
297 | struct dccp_sock *dp = dccp_sk(sk); | ||
298 | /* Check if there is anything to confirm in the first place */ | ||
299 | int yes = !list_empty(&dp->dccps_options.dccpo_conf); | ||
300 | |||
301 | if (!yes) { | ||
302 | struct dccp_opt_pend *opt; | ||
303 | |||
304 | list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, | ||
305 | dccpop_node) { | ||
306 | if (opt->dccpop_conf) { | ||
307 | yes = 1; | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | if (!yes) | ||
314 | return; | ||
315 | |||
316 | /* OK there is something to confirm... */ | ||
317 | /* XXX check if packet is in flight? Send delayed ack?? */ | ||
318 | if (sk->sk_state == DCCP_OPEN) | ||
319 | dccp_send_ack(sk); | ||
320 | } | ||
321 | |||
322 | int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
323 | { | ||
324 | int rc; | ||
325 | |||
326 | dccp_pr_debug("got feat change type=%d feat=%d\n", type, feature); | ||
327 | |||
328 | /* figure out if it's SP or NN feature */ | ||
329 | switch (feature) { | ||
330 | /* deal with SP features */ | ||
331 | case DCCPF_CCID: | ||
332 | rc = dccp_feat_sp(sk, type, feature, val, len); | ||
333 | break; | ||
334 | |||
335 | /* deal with NN features */ | ||
336 | case DCCPF_ACK_RATIO: | ||
337 | rc = dccp_feat_nn(sk, type, feature, val, len); | ||
338 | break; | ||
339 | |||
340 | /* XXX implement other features */ | ||
341 | default: | ||
342 | rc = -EFAULT; | ||
343 | break; | ||
344 | } | ||
345 | |||
346 | /* check if there were problems changing features */ | ||
347 | if (rc) { | ||
348 | /* If we don't agree on SP, we sent a confirm for old value. | ||
349 | * However we propagate rc to caller in case option was | ||
350 | * mandatory | ||
351 | */ | ||
352 | if (rc != DCCP_FEAT_SP_NOAGREE) | ||
353 | dccp_feat_empty_confirm(sk, type, feature); | ||
354 | } | ||
355 | |||
356 | /* generate the confirm [if required] */ | ||
357 | dccp_feat_flush_confirm(sk); | ||
358 | |||
359 | return rc; | ||
360 | } | ||
361 | |||
362 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); | ||
363 | |||
364 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | ||
365 | u8 *val, u8 len) | ||
366 | { | ||
367 | u8 t; | ||
368 | struct dccp_opt_pend *opt; | ||
369 | struct dccp_sock *dp = dccp_sk(sk); | ||
370 | int rc = 1; | ||
371 | int all_confirmed = 1; | ||
372 | |||
373 | dccp_pr_debug("got feat confirm type=%d feat=%d\n", type, feature); | ||
374 | |||
375 | /* XXX sanity check type & feat */ | ||
376 | |||
377 | /* locate our change request */ | ||
378 | t = type == DCCPO_CONFIRM_L ? DCCPO_CHANGE_R : DCCPO_CHANGE_L; | ||
379 | |||
380 | list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, | ||
381 | dccpop_node) { | ||
382 | if (!opt->dccpop_conf && opt->dccpop_type == t && | ||
383 | opt->dccpop_feat == feature) { | ||
384 | /* we found it */ | ||
385 | /* XXX do sanity check */ | ||
386 | |||
387 | opt->dccpop_conf = 1; | ||
388 | |||
389 | /* We got a confirmation---change the option */ | ||
390 | dccp_feat_update(sk, opt->dccpop_type, | ||
391 | opt->dccpop_feat, *val); | ||
392 | |||
393 | dccp_pr_debug("feat %d type %d confirmed %d\n", | ||
394 | feature, type, *val); | ||
395 | rc = 0; | ||
396 | break; | ||
397 | } | ||
398 | |||
399 | if (!opt->dccpop_conf) | ||
400 | all_confirmed = 0; | ||
401 | } | ||
402 | |||
403 | /* fix re-transmit timer */ | ||
404 | /* XXX gotta make sure that no option negotiation occurs during | ||
405 | * connection shutdown. Consider that the CLOSEREQ is sent and timer is | ||
406 | * on. if all options are confirmed it might kill timer which should | ||
407 | * remain alive until close is received. | ||
408 | */ | ||
409 | if (all_confirmed) { | ||
410 | dccp_pr_debug("clear feat negotiation timer %p\n", sk); | ||
411 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); | ||
412 | } | ||
413 | |||
414 | if (rc) | ||
415 | dccp_pr_debug("feat %d type %d never requested\n", | ||
416 | feature, type); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); | ||
421 | |||
422 | void dccp_feat_clean(struct sock *sk) | ||
423 | { | ||
424 | struct dccp_sock *dp = dccp_sk(sk); | ||
425 | struct dccp_opt_pend *opt, *next; | ||
426 | |||
427 | list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_pending, | ||
428 | dccpop_node) { | ||
429 | BUG_ON(opt->dccpop_val == NULL); | ||
430 | kfree(opt->dccpop_val); | ||
431 | |||
432 | if (opt->dccpop_sc != NULL) { | ||
433 | BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); | ||
434 | kfree(opt->dccpop_sc->dccpoc_val); | ||
435 | kfree(opt->dccpop_sc); | ||
436 | } | ||
437 | |||
438 | kfree(opt); | ||
439 | } | ||
440 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_pending); | ||
441 | |||
442 | list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_conf, | ||
443 | dccpop_node) { | ||
444 | BUG_ON(opt == NULL); | ||
445 | if (opt->dccpop_val != NULL) | ||
446 | kfree(opt->dccpop_val); | ||
447 | kfree(opt); | ||
448 | } | ||
449 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf); | ||
450 | } | ||
451 | |||
452 | EXPORT_SYMBOL_GPL(dccp_feat_clean); | ||
453 | |||
454 | /* this is to be called only when a listening sock creates its child. It is | ||
455 | * assumed by the function---the confirm is not duplicated, but rather it is | ||
456 | * "passed on". | ||
457 | */ | ||
458 | int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) | ||
459 | { | ||
460 | struct dccp_sock *olddp = dccp_sk(oldsk); | ||
461 | struct dccp_sock *newdp = dccp_sk(newsk); | ||
462 | struct dccp_opt_pend *opt; | ||
463 | int rc = 0; | ||
464 | |||
465 | INIT_LIST_HEAD(&newdp->dccps_options.dccpo_pending); | ||
466 | INIT_LIST_HEAD(&newdp->dccps_options.dccpo_conf); | ||
467 | |||
468 | list_for_each_entry(opt, &olddp->dccps_options.dccpo_pending, | ||
469 | dccpop_node) { | ||
470 | struct dccp_opt_pend *newopt; | ||
471 | /* copy the value of the option */ | ||
472 | u8 *val = kmalloc(opt->dccpop_len, GFP_ATOMIC); | ||
473 | |||
474 | if (val == NULL) | ||
475 | goto out_clean; | ||
476 | memcpy(val, opt->dccpop_val, opt->dccpop_len); | ||
477 | |||
478 | newopt = kmalloc(sizeof(*newopt), GFP_ATOMIC); | ||
479 | if (newopt == NULL) { | ||
480 | kfree(val); | ||
481 | goto out_clean; | ||
482 | } | ||
483 | |||
484 | /* insert the option */ | ||
485 | memcpy(newopt, opt, sizeof(*newopt)); | ||
486 | newopt->dccpop_val = val; | ||
487 | list_add_tail(&newopt->dccpop_node, | ||
488 | &newdp->dccps_options.dccpo_pending); | ||
489 | |||
490 | /* XXX what happens with backlogs and multiple connections at | ||
491 | * once... | ||
492 | */ | ||
493 | /* the master socket no longer needs to worry about confirms */ | ||
494 | opt->dccpop_sc = 0; /* it's not a memleak---new socket has it */ | ||
495 | |||
496 | /* reset state for a new socket */ | ||
497 | opt->dccpop_conf = 0; | ||
498 | } | ||
499 | |||
500 | /* XXX not doing anything about the conf queue */ | ||
501 | |||
502 | out: | ||
503 | return rc; | ||
504 | |||
505 | out_clean: | ||
506 | dccp_feat_clean(newsk); | ||
507 | rc = -ENOMEM; | ||
508 | goto out; | ||
509 | } | ||
510 | |||
511 | EXPORT_SYMBOL_GPL(dccp_feat_clone); | ||
512 | |||
513 | static int __dccp_feat_init(struct sock *sk, u8 type, u8 feat, u8 *val, u8 len) | ||
514 | { | ||
515 | int rc = -ENOMEM; | ||
516 | u8 *copy = kmalloc(len, GFP_KERNEL); | ||
517 | |||
518 | if (copy != NULL) { | ||
519 | memcpy(copy, val, len); | ||
520 | rc = dccp_feat_change(sk, type, feat, copy, len, GFP_KERNEL); | ||
521 | if (rc) | ||
522 | kfree(copy); | ||
523 | } | ||
524 | return rc; | ||
525 | } | ||
526 | |||
527 | int dccp_feat_init(struct sock *sk) | ||
528 | { | ||
529 | struct dccp_sock *dp = dccp_sk(sk); | ||
530 | int rc; | ||
531 | |||
532 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_pending); | ||
533 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf); | ||
534 | |||
535 | /* CCID L */ | ||
536 | rc = __dccp_feat_init(sk, DCCPO_CHANGE_L, DCCPF_CCID, | ||
537 | &dp->dccps_options.dccpo_tx_ccid, 1); | ||
538 | if (rc) | ||
539 | goto out; | ||
540 | |||
541 | /* CCID R */ | ||
542 | rc = __dccp_feat_init(sk, DCCPO_CHANGE_R, DCCPF_CCID, | ||
543 | &dp->dccps_options.dccpo_rx_ccid, 1); | ||
544 | if (rc) | ||
545 | goto out; | ||
546 | |||
547 | /* Ack ratio */ | ||
548 | rc = __dccp_feat_init(sk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO, | ||
549 | &dp->dccps_options.dccpo_ack_ratio, 1); | ||
550 | out: | ||
551 | return rc; | ||
552 | } | ||
553 | |||
554 | EXPORT_SYMBOL_GPL(dccp_feat_init); | ||
diff --git a/net/dccp/feat.h b/net/dccp/feat.h new file mode 100644 index 000000000000..67da06265f14 --- /dev/null +++ b/net/dccp/feat.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #ifndef _DCCP_FEAT_H | ||
2 | #define _DCCP_FEAT_H | ||
3 | /* | ||
4 | * net/dccp/feat.h | ||
5 | * | ||
6 | * An implementation of the DCCP protocol | ||
7 | * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | |||
16 | struct sock; | ||
17 | |||
18 | extern int dccp_feat_change(struct sock *sk, u8 type, u8 feature, | ||
19 | u8 *val, u8 len, gfp_t gfp); | ||
20 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, | ||
21 | u8 *val, u8 len); | ||
22 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | ||
23 | u8 *val, u8 len); | ||
24 | extern void dccp_feat_clean(struct sock *sk); | ||
25 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); | ||
26 | extern int dccp_feat_init(struct sock *sk); | ||
27 | |||
28 | #endif /* _DCCP_FEAT_H */ | ||
diff --git a/net/dccp/input.c b/net/dccp/input.c index b6cba72b44e8..4b6d43d8b920 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c | |||
@@ -300,6 +300,9 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, | |||
300 | goto out_invalid_packet; | 300 | goto out_invalid_packet; |
301 | } | 301 | } |
302 | 302 | ||
303 | if (dccp_parse_options(sk, skb)) | ||
304 | goto out_invalid_packet; | ||
305 | |||
303 | if (dp->dccps_options.dccpo_send_ack_vector && | 306 | if (dp->dccps_options.dccpo_send_ack_vector && |
304 | dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, | 307 | dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk, |
305 | DCCP_SKB_CB(skb)->dccpd_seq, | 308 | DCCP_SKB_CB(skb)->dccpd_seq, |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 38321ad81875..fcfb486f90c2 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "ackvec.h" | 28 | #include "ackvec.h" |
29 | #include "ccid.h" | 29 | #include "ccid.h" |
30 | #include "dccp.h" | 30 | #include "dccp.h" |
31 | #include "feat.h" | ||
31 | 32 | ||
32 | struct inet_hashinfo __cacheline_aligned dccp_hashinfo = { | 33 | struct inet_hashinfo __cacheline_aligned dccp_hashinfo = { |
33 | .lhash_lock = RW_LOCK_UNLOCKED, | 34 | .lhash_lock = RW_LOCK_UNLOCKED, |
@@ -535,7 +536,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
535 | if (req == NULL) | 536 | if (req == NULL) |
536 | goto drop; | 537 | goto drop; |
537 | 538 | ||
538 | /* FIXME: process options */ | 539 | if (dccp_parse_options(sk, skb)) |
540 | goto drop; | ||
539 | 541 | ||
540 | dccp_openreq_init(req, &dp, skb); | 542 | dccp_openreq_init(req, &dp, skb); |
541 | 543 | ||
@@ -1049,6 +1051,8 @@ int dccp_v4_init_sock(struct sock *sk) | |||
1049 | * setsockopt(CCIDs-I-want/accept). -acme | 1051 | * setsockopt(CCIDs-I-want/accept). -acme |
1050 | */ | 1052 | */ |
1051 | if (likely(!dccp_ctl_socket_init)) { | 1053 | if (likely(!dccp_ctl_socket_init)) { |
1054 | int rc; | ||
1055 | |||
1052 | if (dp->dccps_options.dccpo_send_ack_vector) { | 1056 | if (dp->dccps_options.dccpo_send_ack_vector) { |
1053 | dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL); | 1057 | dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL); |
1054 | if (dp->dccps_hc_rx_ackvec == NULL) | 1058 | if (dp->dccps_hc_rx_ackvec == NULL) |
@@ -1069,8 +1073,16 @@ int dccp_v4_init_sock(struct sock *sk) | |||
1069 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | 1073 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; |
1070 | return -ENOMEM; | 1074 | return -ENOMEM; |
1071 | } | 1075 | } |
1072 | } else | 1076 | |
1077 | rc = dccp_feat_init(sk); | ||
1078 | if (rc) | ||
1079 | return rc; | ||
1080 | } else { | ||
1081 | /* control socket doesn't need feat nego */ | ||
1082 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_pending); | ||
1083 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf); | ||
1073 | dccp_ctl_socket_init = 0; | 1084 | dccp_ctl_socket_init = 0; |
1085 | } | ||
1074 | 1086 | ||
1075 | dccp_init_xmit_timers(sk); | 1087 | dccp_init_xmit_timers(sk); |
1076 | icsk->icsk_rto = DCCP_TIMEOUT_INIT; | 1088 | icsk->icsk_rto = DCCP_TIMEOUT_INIT; |
@@ -1118,6 +1130,9 @@ int dccp_v4_destroy_sock(struct sock *sk) | |||
1118 | ccid_exit(dp->dccps_hc_tx_ccid, sk); | 1130 | ccid_exit(dp->dccps_hc_tx_ccid, sk); |
1119 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | 1131 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; |
1120 | 1132 | ||
1133 | /* clean up feature negotiation state */ | ||
1134 | dccp_feat_clean(sk); | ||
1135 | |||
1121 | return 0; | 1136 | return 0; |
1122 | } | 1137 | } |
1123 | 1138 | ||
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index a60a3e948c36..9e1de5919ee5 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "ackvec.h" | 22 | #include "ackvec.h" |
23 | #include "ccid.h" | 23 | #include "ccid.h" |
24 | #include "dccp.h" | 24 | #include "dccp.h" |
25 | #include "feat.h" | ||
25 | 26 | ||
26 | struct inet_timewait_death_row dccp_death_row = { | 27 | struct inet_timewait_death_row dccp_death_row = { |
27 | .sysctl_max_tw_buckets = NR_FILE * 2, | 28 | .sysctl_max_tw_buckets = NR_FILE * 2, |
@@ -114,6 +115,9 @@ struct sock *dccp_create_openreq_child(struct sock *sk, | |||
114 | newicsk->icsk_rto = DCCP_TIMEOUT_INIT; | 115 | newicsk->icsk_rto = DCCP_TIMEOUT_INIT; |
115 | do_gettimeofday(&newdp->dccps_epoch); | 116 | do_gettimeofday(&newdp->dccps_epoch); |
116 | 117 | ||
118 | if (dccp_feat_clone(sk, newsk)) | ||
119 | goto out_free; | ||
120 | |||
117 | if (newdp->dccps_options.dccpo_send_ack_vector) { | 121 | if (newdp->dccps_options.dccpo_send_ack_vector) { |
118 | newdp->dccps_hc_rx_ackvec = | 122 | newdp->dccps_hc_rx_ackvec = |
119 | dccp_ackvec_alloc(GFP_ATOMIC); | 123 | dccp_ackvec_alloc(GFP_ATOMIC); |
diff --git a/net/dccp/options.c b/net/dccp/options.c index 0a76426c9aea..7f99306c8e99 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c | |||
@@ -21,12 +21,14 @@ | |||
21 | #include "ackvec.h" | 21 | #include "ackvec.h" |
22 | #include "ccid.h" | 22 | #include "ccid.h" |
23 | #include "dccp.h" | 23 | #include "dccp.h" |
24 | #include "feat.h" | ||
24 | 25 | ||
25 | /* stores the default values for new connection. may be changed with sysctl */ | 26 | /* stores the default values for new connection. may be changed with sysctl */ |
26 | static const struct dccp_options dccpo_default_values = { | 27 | static const struct dccp_options dccpo_default_values = { |
27 | .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW, | 28 | .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW, |
28 | .dccpo_rx_ccid = DCCPF_INITIAL_CCID, | 29 | .dccpo_rx_ccid = DCCPF_INITIAL_CCID, |
29 | .dccpo_tx_ccid = DCCPF_INITIAL_CCID, | 30 | .dccpo_tx_ccid = DCCPF_INITIAL_CCID, |
31 | .dccpo_ack_ratio = DCCPF_INITIAL_ACK_RATIO, | ||
30 | .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR, | 32 | .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR, |
31 | .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT, | 33 | .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT, |
32 | }; | 34 | }; |
@@ -69,6 +71,8 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) | |||
69 | unsigned char opt, len; | 71 | unsigned char opt, len; |
70 | unsigned char *value; | 72 | unsigned char *value; |
71 | u32 elapsed_time; | 73 | u32 elapsed_time; |
74 | int rc; | ||
75 | int mandatory = 0; | ||
72 | 76 | ||
73 | memset(opt_recv, 0, sizeof(*opt_recv)); | 77 | memset(opt_recv, 0, sizeof(*opt_recv)); |
74 | 78 | ||
@@ -100,6 +104,11 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) | |||
100 | switch (opt) { | 104 | switch (opt) { |
101 | case DCCPO_PADDING: | 105 | case DCCPO_PADDING: |
102 | break; | 106 | break; |
107 | case DCCPO_MANDATORY: | ||
108 | if (mandatory) | ||
109 | goto out_invalid_option; | ||
110 | mandatory = 1; | ||
111 | break; | ||
103 | case DCCPO_NDP_COUNT: | 112 | case DCCPO_NDP_COUNT: |
104 | if (len > 3) | 113 | if (len > 3) |
105 | goto out_invalid_option; | 114 | goto out_invalid_option; |
@@ -108,6 +117,31 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) | |||
108 | dccp_pr_debug("%sNDP count=%d\n", debug_prefix, | 117 | dccp_pr_debug("%sNDP count=%d\n", debug_prefix, |
109 | opt_recv->dccpor_ndp); | 118 | opt_recv->dccpor_ndp); |
110 | break; | 119 | break; |
120 | case DCCPO_CHANGE_L: | ||
121 | /* fall through */ | ||
122 | case DCCPO_CHANGE_R: | ||
123 | if (len < 2) | ||
124 | goto out_invalid_option; | ||
125 | rc = dccp_feat_change_recv(sk, opt, *value, value + 1, | ||
126 | len - 1); | ||
127 | /* | ||
128 | * When there is a change error, change_recv is | ||
129 | * responsible for dealing with it. i.e. reply with an | ||
130 | * empty confirm. | ||
131 | * If the change was mandatory, then we need to die. | ||
132 | */ | ||
133 | if (rc && mandatory) | ||
134 | goto out_invalid_option; | ||
135 | break; | ||
136 | case DCCPO_CONFIRM_L: | ||
137 | /* fall through */ | ||
138 | case DCCPO_CONFIRM_R: | ||
139 | if (len < 2) | ||
140 | goto out_invalid_option; | ||
141 | if (dccp_feat_confirm_recv(sk, opt, *value, | ||
142 | value + 1, len - 1)) | ||
143 | goto out_invalid_option; | ||
144 | break; | ||
111 | case DCCPO_ACK_VECTOR_0: | 145 | case DCCPO_ACK_VECTOR_0: |
112 | case DCCPO_ACK_VECTOR_1: | 146 | case DCCPO_ACK_VECTOR_1: |
113 | if (pkt_type == DCCP_PKT_DATA) | 147 | if (pkt_type == DCCP_PKT_DATA) |
@@ -208,6 +242,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb) | |||
208 | sk, opt, len); | 242 | sk, opt, len); |
209 | break; | 243 | break; |
210 | } | 244 | } |
245 | |||
246 | if (opt != DCCPO_MANDATORY) | ||
247 | mandatory = 0; | ||
211 | } | 248 | } |
212 | 249 | ||
213 | return 0; | 250 | return 0; |
@@ -356,7 +393,7 @@ void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb) | |||
356 | { | 393 | { |
357 | struct timeval tv; | 394 | struct timeval tv; |
358 | u32 now; | 395 | u32 now; |
359 | 396 | ||
360 | dccp_timestamp(sk, &tv); | 397 | dccp_timestamp(sk, &tv); |
361 | now = timeval_usecs(&tv) / 10; | 398 | now = timeval_usecs(&tv) / 10; |
362 | /* yes this will overflow but that is the point as we want a | 399 | /* yes this will overflow but that is the point as we want a |
@@ -402,7 +439,7 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk, | |||
402 | tstamp_echo = htonl(dp->dccps_timestamp_echo); | 439 | tstamp_echo = htonl(dp->dccps_timestamp_echo); |
403 | memcpy(to, &tstamp_echo, 4); | 440 | memcpy(to, &tstamp_echo, 4); |
404 | to += 4; | 441 | to += 4; |
405 | 442 | ||
406 | if (elapsed_time_len == 2) { | 443 | if (elapsed_time_len == 2) { |
407 | const u16 var16 = htons((u16)elapsed_time); | 444 | const u16 var16 = htons((u16)elapsed_time); |
408 | memcpy(to, &var16, 2); | 445 | memcpy(to, &var16, 2); |
@@ -421,6 +458,93 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk, | |||
421 | dp->dccps_timestamp_time.tv_usec = 0; | 458 | dp->dccps_timestamp_time.tv_usec = 0; |
422 | } | 459 | } |
423 | 460 | ||
461 | static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat, | ||
462 | u8 *val, u8 len) | ||
463 | { | ||
464 | u8 *to; | ||
465 | |||
466 | if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) { | ||
467 | LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small" | ||
468 | " to insert feature %d option!\n", feat); | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3; | ||
473 | |||
474 | to = skb_push(skb, len + 3); | ||
475 | *to++ = type; | ||
476 | *to++ = len + 3; | ||
477 | *to++ = feat; | ||
478 | |||
479 | if (len) | ||
480 | memcpy(to, val, len); | ||
481 | dccp_pr_debug("option %d feat %d len %d\n", type, feat, len); | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static void dccp_insert_feat(struct sock *sk, struct sk_buff *skb) | ||
487 | { | ||
488 | struct dccp_sock *dp = dccp_sk(sk); | ||
489 | struct dccp_opt_pend *opt, *next; | ||
490 | int change = 0; | ||
491 | |||
492 | /* confirm any options [NN opts] */ | ||
493 | list_for_each_entry_safe(opt, next, &dp->dccps_options.dccpo_conf, | ||
494 | dccpop_node) { | ||
495 | dccp_insert_feat_opt(skb, opt->dccpop_type, | ||
496 | opt->dccpop_feat, opt->dccpop_val, | ||
497 | opt->dccpop_len); | ||
498 | /* fear empty confirms */ | ||
499 | if (opt->dccpop_val) | ||
500 | kfree(opt->dccpop_val); | ||
501 | kfree(opt); | ||
502 | } | ||
503 | INIT_LIST_HEAD(&dp->dccps_options.dccpo_conf); | ||
504 | |||
505 | /* see which features we need to send */ | ||
506 | list_for_each_entry(opt, &dp->dccps_options.dccpo_pending, | ||
507 | dccpop_node) { | ||
508 | /* see if we need to send any confirm */ | ||
509 | if (opt->dccpop_sc) { | ||
510 | dccp_insert_feat_opt(skb, opt->dccpop_type + 1, | ||
511 | opt->dccpop_feat, | ||
512 | opt->dccpop_sc->dccpoc_val, | ||
513 | opt->dccpop_sc->dccpoc_len); | ||
514 | |||
515 | BUG_ON(!opt->dccpop_sc->dccpoc_val); | ||
516 | kfree(opt->dccpop_sc->dccpoc_val); | ||
517 | kfree(opt->dccpop_sc); | ||
518 | opt->dccpop_sc = NULL; | ||
519 | } | ||
520 | |||
521 | /* any option not confirmed, re-send it */ | ||
522 | if (!opt->dccpop_conf) { | ||
523 | dccp_insert_feat_opt(skb, opt->dccpop_type, | ||
524 | opt->dccpop_feat, opt->dccpop_val, | ||
525 | opt->dccpop_len); | ||
526 | change++; | ||
527 | } | ||
528 | } | ||
529 | |||
530 | /* Retransmit timer. | ||
531 | * If this is the master listening sock, we don't set a timer on it. It | ||
532 | * should be fine because if the dude doesn't receive our RESPONSE | ||
533 | * [which will contain the CHANGE] he will send another REQUEST which | ||
534 | * will "retrnasmit" the change. | ||
535 | */ | ||
536 | if (change && dp->dccps_role != DCCP_ROLE_LISTEN) { | ||
537 | dccp_pr_debug("reset feat negotiation timer %p\n", sk); | ||
538 | |||
539 | /* XXX don't reset the timer on re-transmissions. I.e. reset it | ||
540 | * only when sending new stuff i guess. Currently the timer | ||
541 | * never backs off because on re-transmission it just resets it! | ||
542 | */ | ||
543 | inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, | ||
544 | inet_csk(sk)->icsk_rto, DCCP_RTO_MAX); | ||
545 | } | ||
546 | } | ||
547 | |||
424 | void dccp_insert_options(struct sock *sk, struct sk_buff *skb) | 548 | void dccp_insert_options(struct sock *sk, struct sk_buff *skb) |
425 | { | 549 | { |
426 | struct dccp_sock *dp = dccp_sk(sk); | 550 | struct dccp_sock *dp = dccp_sk(sk); |
@@ -447,6 +571,17 @@ void dccp_insert_options(struct sock *sk, struct sk_buff *skb) | |||
447 | dp->dccps_hc_tx_insert_options = 0; | 571 | dp->dccps_hc_tx_insert_options = 0; |
448 | } | 572 | } |
449 | 573 | ||
574 | /* Feature negotiation */ | ||
575 | switch(DCCP_SKB_CB(skb)->dccpd_type) { | ||
576 | /* Data packets can't do feat negotiation */ | ||
577 | case DCCP_PKT_DATA: | ||
578 | case DCCP_PKT_DATAACK: | ||
579 | break; | ||
580 | default: | ||
581 | dccp_insert_feat(sk, skb); | ||
582 | break; | ||
583 | } | ||
584 | |||
450 | /* XXX: insert other options when appropriate */ | 585 | /* XXX: insert other options when appropriate */ |
451 | 586 | ||
452 | if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) { | 587 | if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) { |
diff --git a/net/dccp/output.c b/net/dccp/output.c index efd7ffb903a1..0cc2bcf56522 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c | |||
@@ -64,6 +64,10 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb) | |||
64 | case DCCP_PKT_DATAACK: | 64 | case DCCP_PKT_DATAACK: |
65 | break; | 65 | break; |
66 | 66 | ||
67 | case DCCP_PKT_REQUEST: | ||
68 | set_ack = 0; | ||
69 | /* fall through */ | ||
70 | |||
67 | case DCCP_PKT_SYNC: | 71 | case DCCP_PKT_SYNC: |
68 | case DCCP_PKT_SYNCACK: | 72 | case DCCP_PKT_SYNCACK: |
69 | ackno = dcb->dccpd_seq; | 73 | ackno = dcb->dccpd_seq; |
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 81ad24953710..1a8cf8ecfe63 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | #include "ccid.h" | 38 | #include "ccid.h" |
39 | #include "dccp.h" | 39 | #include "dccp.h" |
40 | #include "feat.h" | ||
40 | 41 | ||
41 | DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; | 42 | DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; |
42 | 43 | ||
@@ -255,6 +256,39 @@ static int dccp_setsockopt_service(struct sock *sk, const u32 service, | |||
255 | return 0; | 256 | return 0; |
256 | } | 257 | } |
257 | 258 | ||
259 | /* byte 1 is feature. the rest is the preference list */ | ||
260 | static int dccp_setsockopt_change(struct sock *sk, int type, | ||
261 | struct dccp_so_feat __user *optval) | ||
262 | { | ||
263 | struct dccp_so_feat opt; | ||
264 | u8 *val; | ||
265 | int rc; | ||
266 | |||
267 | if (copy_from_user(&opt, optval, sizeof(opt))) | ||
268 | return -EFAULT; | ||
269 | |||
270 | val = kmalloc(opt.dccpsf_len, GFP_KERNEL); | ||
271 | if (!val) | ||
272 | return -ENOMEM; | ||
273 | |||
274 | if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { | ||
275 | rc = -EFAULT; | ||
276 | goto out_free_val; | ||
277 | } | ||
278 | |||
279 | rc = dccp_feat_change(sk, type, opt.dccpsf_feat, val, opt.dccpsf_len, | ||
280 | GFP_KERNEL); | ||
281 | if (rc) | ||
282 | goto out_free_val; | ||
283 | |||
284 | out: | ||
285 | return rc; | ||
286 | |||
287 | out_free_val: | ||
288 | kfree(val); | ||
289 | goto out; | ||
290 | } | ||
291 | |||
258 | int dccp_setsockopt(struct sock *sk, int level, int optname, | 292 | int dccp_setsockopt(struct sock *sk, int level, int optname, |
259 | char __user *optval, int optlen) | 293 | char __user *optval, int optlen) |
260 | { | 294 | { |
@@ -284,6 +318,25 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, | |||
284 | case DCCP_SOCKOPT_PACKET_SIZE: | 318 | case DCCP_SOCKOPT_PACKET_SIZE: |
285 | dp->dccps_packet_size = val; | 319 | dp->dccps_packet_size = val; |
286 | break; | 320 | break; |
321 | |||
322 | case DCCP_SOCKOPT_CHANGE_L: | ||
323 | if (optlen != sizeof(struct dccp_so_feat)) | ||
324 | err = -EINVAL; | ||
325 | else | ||
326 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, | ||
327 | (struct dccp_so_feat *) | ||
328 | optval); | ||
329 | break; | ||
330 | |||
331 | case DCCP_SOCKOPT_CHANGE_R: | ||
332 | if (optlen != sizeof(struct dccp_so_feat)) | ||
333 | err = -EINVAL; | ||
334 | else | ||
335 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, | ||
336 | (struct dccp_so_feat *) | ||
337 | optval); | ||
338 | break; | ||
339 | |||
287 | default: | 340 | default: |
288 | err = -ENOPROTOOPT; | 341 | err = -ENOPROTOOPT; |
289 | break; | 342 | break; |
diff --git a/net/dccp/timer.c b/net/dccp/timer.c index aa34b576e228..d7c786608ec6 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c | |||
@@ -141,6 +141,17 @@ static void dccp_retransmit_timer(struct sock *sk) | |||
141 | { | 141 | { |
142 | struct inet_connection_sock *icsk = inet_csk(sk); | 142 | struct inet_connection_sock *icsk = inet_csk(sk); |
143 | 143 | ||
144 | /* retransmit timer is used for feature negotiation throughout | ||
145 | * connection. In this case, no packet is re-transmitted, but rather an | ||
146 | * ack is generated and pending changes are splaced into its options. | ||
147 | */ | ||
148 | if (sk->sk_send_head == NULL) { | ||
149 | dccp_pr_debug("feat negotiation retransmit timeout %p\n", sk); | ||
150 | if (sk->sk_state == DCCP_OPEN) | ||
151 | dccp_send_ack(sk); | ||
152 | goto backoff; | ||
153 | } | ||
154 | |||
144 | /* | 155 | /* |
145 | * sk->sk_send_head has to have one skb with | 156 | * sk->sk_send_head has to have one skb with |
146 | * DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP | 157 | * DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP |
@@ -177,6 +188,7 @@ static void dccp_retransmit_timer(struct sock *sk) | |||
177 | goto out; | 188 | goto out; |
178 | } | 189 | } |
179 | 190 | ||
191 | backoff: | ||
180 | icsk->icsk_backoff++; | 192 | icsk->icsk_backoff++; |
181 | icsk->icsk_retransmits++; | 193 | icsk->icsk_retransmits++; |
182 | 194 | ||