diff options
Diffstat (limited to 'net/dccp/feat.c')
-rw-r--r-- | net/dccp/feat.c | 131 |
1 files changed, 95 insertions, 36 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index a1b0682ee77c..4dc487f27a1f 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -12,7 +12,6 @@ | |||
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | 14 | ||
15 | #include "dccp.h" | ||
16 | #include "ccid.h" | 15 | #include "ccid.h" |
17 | #include "feat.h" | 16 | #include "feat.h" |
18 | 17 | ||
@@ -23,9 +22,17 @@ int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | |||
23 | { | 22 | { |
24 | struct dccp_opt_pend *opt; | 23 | struct dccp_opt_pend *opt; |
25 | 24 | ||
26 | dccp_pr_debug("feat change type=%d feat=%d\n", type, feature); | 25 | dccp_feat_debug(type, feature, *val); |
27 | 26 | ||
28 | /* XXX sanity check feat change request */ | 27 | if (!dccp_feat_is_valid_type(type)) { |
28 | DCCP_WARN("option type %d invalid in negotiation\n", type); | ||
29 | return 1; | ||
30 | } | ||
31 | if (!dccp_feat_is_valid_length(type, feature, len)) { | ||
32 | DCCP_WARN("invalid length %d\n", len); | ||
33 | return 1; | ||
34 | } | ||
35 | /* XXX add further sanity checks */ | ||
29 | 36 | ||
30 | /* check if that feature is already being negotiated */ | 37 | /* check if that feature is already being negotiated */ |
31 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | 38 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { |
@@ -95,14 +102,14 @@ static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) | |||
95 | /* XXX taking only u8 vals */ | 102 | /* XXX taking only u8 vals */ |
96 | static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) | 103 | static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) |
97 | { | 104 | { |
98 | dccp_pr_debug("changing [%d] feat %d to %d\n", type, feat, val); | 105 | dccp_feat_debug(type, feat, val); |
99 | 106 | ||
100 | switch (feat) { | 107 | switch (feat) { |
101 | case DCCPF_CCID: | 108 | case DCCPF_CCID: |
102 | return dccp_feat_update_ccid(sk, type, val); | 109 | return dccp_feat_update_ccid(sk, type, val); |
103 | default: | 110 | default: |
104 | dccp_pr_debug("IMPLEMENT changing [%d] feat %d to %d\n", | 111 | dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n", |
105 | type, feat, val); | 112 | dccp_feat_typename(type), feat); |
106 | break; | 113 | break; |
107 | } | 114 | } |
108 | return 0; | 115 | return 0; |
@@ -162,7 +169,8 @@ static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, | |||
162 | break; | 169 | break; |
163 | 170 | ||
164 | default: | 171 | default: |
165 | WARN_ON(1); /* XXX implement res */ | 172 | DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat); |
173 | /* XXX implement res */ | ||
166 | return -EFAULT; | 174 | return -EFAULT; |
167 | } | 175 | } |
168 | 176 | ||
@@ -265,10 +273,10 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
265 | u8 *copy; | 273 | u8 *copy; |
266 | int rc; | 274 | int rc; |
267 | 275 | ||
268 | /* NN features must be change L */ | 276 | /* NN features must be Change L (sec. 6.3.2) */ |
269 | if (type == DCCPO_CHANGE_R) { | 277 | if (type != DCCPO_CHANGE_L) { |
270 | dccp_pr_debug("received CHANGE_R %d for NN feat %d\n", | 278 | dccp_pr_debug("received %s for NN feature %d\n", |
271 | type, feature); | 279 | dccp_feat_typename(type), feature); |
272 | return -EFAULT; | 280 | return -EFAULT; |
273 | } | 281 | } |
274 | 282 | ||
@@ -279,12 +287,11 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
279 | if (opt == NULL) | 287 | if (opt == NULL) |
280 | return -ENOMEM; | 288 | return -ENOMEM; |
281 | 289 | ||
282 | copy = kmalloc(len, GFP_ATOMIC); | 290 | copy = kmemdup(val, len, GFP_ATOMIC); |
283 | if (copy == NULL) { | 291 | if (copy == NULL) { |
284 | kfree(opt); | 292 | kfree(opt); |
285 | return -ENOMEM; | 293 | return -ENOMEM; |
286 | } | 294 | } |
287 | memcpy(copy, val, len); | ||
288 | 295 | ||
289 | opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ | 296 | opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ |
290 | opt->dccpop_feat = feature; | 297 | opt->dccpop_feat = feature; |
@@ -299,7 +306,8 @@ static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
299 | return rc; | 306 | return rc; |
300 | } | 307 | } |
301 | 308 | ||
302 | dccp_pr_debug("Confirming NN feature %d (val=%d)\n", feature, *copy); | 309 | dccp_feat_debug(type, feature, *copy); |
310 | |||
303 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); | 311 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); |
304 | 312 | ||
305 | return 0; | 313 | return 0; |
@@ -318,14 +326,19 @@ static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, | |||
318 | return; | 326 | return; |
319 | } | 327 | } |
320 | 328 | ||
321 | opt->dccpop_type = type == DCCPO_CHANGE_L ? DCCPO_CONFIRM_R : | 329 | switch (type) { |
322 | DCCPO_CONFIRM_L; | 330 | case DCCPO_CHANGE_L: opt->dccpop_type = DCCPO_CONFIRM_R; break; |
331 | case DCCPO_CHANGE_R: opt->dccpop_type = DCCPO_CONFIRM_L; break; | ||
332 | default: DCCP_WARN("invalid type %d\n", type); return; | ||
333 | |||
334 | } | ||
323 | opt->dccpop_feat = feature; | 335 | opt->dccpop_feat = feature; |
324 | opt->dccpop_val = NULL; | 336 | opt->dccpop_val = NULL; |
325 | opt->dccpop_len = 0; | 337 | opt->dccpop_len = 0; |
326 | 338 | ||
327 | /* change feature */ | 339 | /* change feature */ |
328 | dccp_pr_debug("Empty confirm feature %d type %d\n", feature, type); | 340 | dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature); |
341 | |||
329 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); | 342 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); |
330 | } | 343 | } |
331 | 344 | ||
@@ -359,7 +372,7 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
359 | { | 372 | { |
360 | int rc; | 373 | int rc; |
361 | 374 | ||
362 | dccp_pr_debug("got feat change type=%d feat=%d\n", type, feature); | 375 | dccp_feat_debug(type, feature, *val); |
363 | 376 | ||
364 | /* figure out if it's SP or NN feature */ | 377 | /* figure out if it's SP or NN feature */ |
365 | switch (feature) { | 378 | switch (feature) { |
@@ -375,6 +388,8 @@ int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | |||
375 | 388 | ||
376 | /* XXX implement other features */ | 389 | /* XXX implement other features */ |
377 | default: | 390 | default: |
391 | dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n", | ||
392 | dccp_feat_typename(type), feature); | ||
378 | rc = -EFAULT; | 393 | rc = -EFAULT; |
379 | break; | 394 | break; |
380 | } | 395 | } |
@@ -403,20 +418,27 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | |||
403 | u8 t; | 418 | u8 t; |
404 | struct dccp_opt_pend *opt; | 419 | struct dccp_opt_pend *opt; |
405 | struct dccp_minisock *dmsk = dccp_msk(sk); | 420 | struct dccp_minisock *dmsk = dccp_msk(sk); |
406 | int rc = 1; | 421 | int found = 0; |
407 | int all_confirmed = 1; | 422 | int all_confirmed = 1; |
408 | 423 | ||
409 | dccp_pr_debug("got feat confirm type=%d feat=%d\n", type, feature); | 424 | dccp_feat_debug(type, feature, *val); |
410 | |||
411 | /* XXX sanity check type & feat */ | ||
412 | 425 | ||
413 | /* locate our change request */ | 426 | /* locate our change request */ |
414 | t = type == DCCPO_CONFIRM_L ? DCCPO_CHANGE_R : DCCPO_CHANGE_L; | 427 | switch (type) { |
428 | case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break; | ||
429 | case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break; | ||
430 | default: DCCP_WARN("invalid type %d\n", type); | ||
431 | return 1; | ||
432 | |||
433 | } | ||
434 | /* XXX sanity check feature value */ | ||
415 | 435 | ||
416 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | 436 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { |
417 | if (!opt->dccpop_conf && opt->dccpop_type == t && | 437 | if (!opt->dccpop_conf && opt->dccpop_type == t && |
418 | opt->dccpop_feat == feature) { | 438 | opt->dccpop_feat == feature) { |
419 | /* we found it */ | 439 | found = 1; |
440 | dccp_pr_debug("feature %d found\n", opt->dccpop_feat); | ||
441 | |||
420 | /* XXX do sanity check */ | 442 | /* XXX do sanity check */ |
421 | 443 | ||
422 | opt->dccpop_conf = 1; | 444 | opt->dccpop_conf = 1; |
@@ -425,9 +447,7 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | |||
425 | dccp_feat_update(sk, opt->dccpop_type, | 447 | dccp_feat_update(sk, opt->dccpop_type, |
426 | opt->dccpop_feat, *val); | 448 | opt->dccpop_feat, *val); |
427 | 449 | ||
428 | dccp_pr_debug("feat %d type %d confirmed %d\n", | 450 | /* XXX check the return value of dccp_feat_update */ |
429 | feature, type, *val); | ||
430 | rc = 0; | ||
431 | break; | 451 | break; |
432 | } | 452 | } |
433 | 453 | ||
@@ -446,9 +466,9 @@ int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | |||
446 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); | 466 | inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS); |
447 | } | 467 | } |
448 | 468 | ||
449 | if (rc) | 469 | if (!found) |
450 | dccp_pr_debug("feat %d type %d never requested\n", | 470 | dccp_pr_debug("%s(%d, ...) never requested\n", |
451 | feature, type); | 471 | dccp_feat_typename(type), feature); |
452 | return 0; | 472 | return 0; |
453 | } | 473 | } |
454 | 474 | ||
@@ -501,20 +521,18 @@ int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) | |||
501 | list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { | 521 | list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { |
502 | struct dccp_opt_pend *newopt; | 522 | struct dccp_opt_pend *newopt; |
503 | /* copy the value of the option */ | 523 | /* copy the value of the option */ |
504 | u8 *val = kmalloc(opt->dccpop_len, GFP_ATOMIC); | 524 | u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC); |
505 | 525 | ||
506 | if (val == NULL) | 526 | if (val == NULL) |
507 | goto out_clean; | 527 | goto out_clean; |
508 | memcpy(val, opt->dccpop_val, opt->dccpop_len); | ||
509 | 528 | ||
510 | newopt = kmalloc(sizeof(*newopt), GFP_ATOMIC); | 529 | newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC); |
511 | if (newopt == NULL) { | 530 | if (newopt == NULL) { |
512 | kfree(val); | 531 | kfree(val); |
513 | goto out_clean; | 532 | goto out_clean; |
514 | } | 533 | } |
515 | 534 | ||
516 | /* insert the option */ | 535 | /* insert the option */ |
517 | memcpy(newopt, opt, sizeof(*newopt)); | ||
518 | newopt->dccpop_val = val; | 536 | newopt->dccpop_val = val; |
519 | list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); | 537 | list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); |
520 | 538 | ||
@@ -545,10 +563,9 @@ static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat, | |||
545 | u8 *val, u8 len) | 563 | u8 *val, u8 len) |
546 | { | 564 | { |
547 | int rc = -ENOMEM; | 565 | int rc = -ENOMEM; |
548 | u8 *copy = kmalloc(len, GFP_KERNEL); | 566 | u8 *copy = kmemdup(val, len, GFP_KERNEL); |
549 | 567 | ||
550 | if (copy != NULL) { | 568 | if (copy != NULL) { |
551 | memcpy(copy, val, len); | ||
552 | rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL); | 569 | rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL); |
553 | if (rc) | 570 | if (rc) |
554 | kfree(copy); | 571 | kfree(copy); |
@@ -583,3 +600,45 @@ out: | |||
583 | } | 600 | } |
584 | 601 | ||
585 | EXPORT_SYMBOL_GPL(dccp_feat_init); | 602 | EXPORT_SYMBOL_GPL(dccp_feat_init); |
603 | |||
604 | #ifdef CONFIG_IP_DCCP_DEBUG | ||
605 | const char *dccp_feat_typename(const u8 type) | ||
606 | { | ||
607 | switch(type) { | ||
608 | case DCCPO_CHANGE_L: return("ChangeL"); | ||
609 | case DCCPO_CONFIRM_L: return("ConfirmL"); | ||
610 | case DCCPO_CHANGE_R: return("ChangeR"); | ||
611 | case DCCPO_CONFIRM_R: return("ConfirmR"); | ||
612 | /* the following case must not appear in feature negotation */ | ||
613 | default: dccp_pr_debug("unknown type %d [BUG!]\n", type); | ||
614 | } | ||
615 | return NULL; | ||
616 | } | ||
617 | |||
618 | EXPORT_SYMBOL_GPL(dccp_feat_typename); | ||
619 | |||
620 | const char *dccp_feat_name(const u8 feat) | ||
621 | { | ||
622 | static const char *feature_names[] = { | ||
623 | [DCCPF_RESERVED] = "Reserved", | ||
624 | [DCCPF_CCID] = "CCID", | ||
625 | [DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos", | ||
626 | [DCCPF_SEQUENCE_WINDOW] = "Sequence Window", | ||
627 | [DCCPF_ECN_INCAPABLE] = "ECN Incapable", | ||
628 | [DCCPF_ACK_RATIO] = "Ack Ratio", | ||
629 | [DCCPF_SEND_ACK_VECTOR] = "Send ACK Vector", | ||
630 | [DCCPF_SEND_NDP_COUNT] = "Send NDP Count", | ||
631 | [DCCPF_MIN_CSUM_COVER] = "Min. Csum Coverage", | ||
632 | [DCCPF_DATA_CHECKSUM] = "Send Data Checksum", | ||
633 | }; | ||
634 | if (feat >= DCCPF_MIN_CCID_SPECIFIC) | ||
635 | return "CCID-specific"; | ||
636 | |||
637 | if (dccp_feat_is_reserved(feat)) | ||
638 | return feature_names[DCCPF_RESERVED]; | ||
639 | |||
640 | return feature_names[feat]; | ||
641 | } | ||
642 | |||
643 | EXPORT_SYMBOL_GPL(dccp_feat_name); | ||
644 | #endif /* CONFIG_IP_DCCP_DEBUG */ | ||