]> bbs.cooldavid.org Git - net-next-2.6.git/blob - net/dccp/feat.c
[DCCP]: Initial feature negotiation implementation
[net-next-2.6.git] / net / dccp / feat.c
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);