diff options
-rw-r--r-- | net/dccp/dccp.h | 1 | ||||
-rw-r--r-- | net/dccp/feat.c | 160 | ||||
-rw-r--r-- | net/dccp/output.c | 4 | ||||
-rw-r--r-- | net/dccp/proto.c | 3 |
4 files changed, 168 insertions, 0 deletions
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index dee4a90886d6..1881527bdcd7 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h | |||
@@ -442,6 +442,7 @@ static inline int dccp_ack_pending(const struct sock *sk) | |||
442 | inet_csk_ack_scheduled(sk); | 442 | inet_csk_ack_scheduled(sk); |
443 | } | 443 | } |
444 | 444 | ||
445 | extern int dccp_feat_finalise_settings(struct dccp_sock *dp); | ||
445 | extern void dccp_feat_list_purge(struct list_head *fn_list); | 446 | extern void dccp_feat_list_purge(struct list_head *fn_list); |
446 | 447 | ||
447 | extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb); | 448 | extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb); |
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 9399554878cc..ed9f50b1c34a 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -441,6 +441,166 @@ int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | |||
441 | 441 | ||
442 | EXPORT_SYMBOL_GPL(dccp_feat_change); | 442 | EXPORT_SYMBOL_GPL(dccp_feat_change); |
443 | 443 | ||
444 | /* | ||
445 | * Tracking features whose value depend on the choice of CCID | ||
446 | * | ||
447 | * This is designed with an extension in mind so that a list walk could be done | ||
448 | * before activating any features. However, the existing framework was found to | ||
449 | * work satisfactorily up until now, the automatic verification is left open. | ||
450 | * When adding new CCIDs, add a corresponding dependency table here. | ||
451 | */ | ||
452 | static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local) | ||
453 | { | ||
454 | static const struct ccid_dependency ccid2_dependencies[2][2] = { | ||
455 | /* | ||
456 | * CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX | ||
457 | * feature and Send Ack Vector is an RX feature, `is_local' | ||
458 | * needs to be reversed. | ||
459 | */ | ||
460 | { /* Dependencies of the receiver-side (remote) CCID2 */ | ||
461 | { | ||
462 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
463 | .is_local = true, | ||
464 | .is_mandatory = true, | ||
465 | .val = 1 | ||
466 | }, | ||
467 | { 0, 0, 0, 0 } | ||
468 | }, | ||
469 | { /* Dependencies of the sender-side (local) CCID2 */ | ||
470 | { | ||
471 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
472 | .is_local = false, | ||
473 | .is_mandatory = true, | ||
474 | .val = 1 | ||
475 | }, | ||
476 | { 0, 0, 0, 0 } | ||
477 | } | ||
478 | }; | ||
479 | static const struct ccid_dependency ccid3_dependencies[2][5] = { | ||
480 | { /* | ||
481 | * Dependencies of the receiver-side CCID3 | ||
482 | */ | ||
483 | { /* locally disable Ack Vectors */ | ||
484 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
485 | .is_local = true, | ||
486 | .is_mandatory = false, | ||
487 | .val = 0 | ||
488 | }, | ||
489 | { /* see below why Send Loss Event Rate is on */ | ||
490 | .dependent_feat = DCCPF_SEND_LEV_RATE, | ||
491 | .is_local = true, | ||
492 | .is_mandatory = true, | ||
493 | .val = 1 | ||
494 | }, | ||
495 | { /* NDP Count is needed as per RFC 4342, 6.1.1 */ | ||
496 | .dependent_feat = DCCPF_SEND_NDP_COUNT, | ||
497 | .is_local = false, | ||
498 | .is_mandatory = true, | ||
499 | .val = 1 | ||
500 | }, | ||
501 | { 0, 0, 0, 0 }, | ||
502 | }, | ||
503 | { /* | ||
504 | * CCID3 at the TX side: we request that the HC-receiver | ||
505 | * will not send Ack Vectors (they will be ignored, so | ||
506 | * Mandatory is not set); we enable Send Loss Event Rate | ||
507 | * (Mandatory since the implementation does not support | ||
508 | * the Loss Intervals option of RFC 4342, 8.6). | ||
509 | * The last two options are for peer's information only. | ||
510 | */ | ||
511 | { | ||
512 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
513 | .is_local = false, | ||
514 | .is_mandatory = false, | ||
515 | .val = 0 | ||
516 | }, | ||
517 | { | ||
518 | .dependent_feat = DCCPF_SEND_LEV_RATE, | ||
519 | .is_local = false, | ||
520 | .is_mandatory = true, | ||
521 | .val = 1 | ||
522 | }, | ||
523 | { /* this CCID does not support Ack Ratio */ | ||
524 | .dependent_feat = DCCPF_ACK_RATIO, | ||
525 | .is_local = true, | ||
526 | .is_mandatory = false, | ||
527 | .val = 0 | ||
528 | }, | ||
529 | { /* tell receiver we are sending NDP counts */ | ||
530 | .dependent_feat = DCCPF_SEND_NDP_COUNT, | ||
531 | .is_local = true, | ||
532 | .is_mandatory = false, | ||
533 | .val = 1 | ||
534 | }, | ||
535 | { 0, 0, 0, 0 } | ||
536 | } | ||
537 | }; | ||
538 | switch (ccid) { | ||
539 | case DCCPC_CCID2: | ||
540 | return ccid2_dependencies[is_local]; | ||
541 | case DCCPC_CCID3: | ||
542 | return ccid3_dependencies[is_local]; | ||
543 | default: | ||
544 | return NULL; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | /** | ||
549 | * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID | ||
550 | * @fn: feature-negotiation list to update | ||
551 | * @id: CCID number to track | ||
552 | * @is_local: whether TX CCID (1) or RX CCID (0) is meant | ||
553 | * This function needs to be called after registering all other features. | ||
554 | */ | ||
555 | static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local) | ||
556 | { | ||
557 | const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local); | ||
558 | int i, rc = (table == NULL); | ||
559 | |||
560 | for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++) | ||
561 | if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP) | ||
562 | rc = __feat_register_sp(fn, table[i].dependent_feat, | ||
563 | table[i].is_local, | ||
564 | table[i].is_mandatory, | ||
565 | &table[i].val, 1); | ||
566 | else | ||
567 | rc = __feat_register_nn(fn, table[i].dependent_feat, | ||
568 | table[i].is_mandatory, | ||
569 | table[i].val); | ||
570 | return rc; | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * dccp_feat_finalise_settings - Finalise settings before starting negotiation | ||
575 | * @dp: client or listening socket (settings will be inherited) | ||
576 | * This is called after all registrations (socket initialisation, sysctls, and | ||
577 | * sockopt calls), and before sending the first packet containing Change options | ||
578 | * (ie. client-Request or server-Response), to ensure internal consistency. | ||
579 | */ | ||
580 | int dccp_feat_finalise_settings(struct dccp_sock *dp) | ||
581 | { | ||
582 | struct list_head *fn = &dp->dccps_featneg; | ||
583 | struct dccp_feat_entry *entry; | ||
584 | int i = 2, ccids[2] = { -1, -1 }; | ||
585 | |||
586 | /* | ||
587 | * Propagating CCIDs: | ||
588 | * 1) not useful to propagate CCID settings if this host advertises more | ||
589 | * than one CCID: the choice of CCID may still change - if this is | ||
590 | * the client, or if this is the server and the client sends | ||
591 | * singleton CCID values. | ||
592 | * 2) since is that propagate_ccid changes the list, we defer changing | ||
593 | * the sorted list until after the traversal. | ||
594 | */ | ||
595 | list_for_each_entry(entry, fn, node) | ||
596 | if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1) | ||
597 | ccids[entry->is_local] = entry->val.sp.vec[0]; | ||
598 | while (i--) | ||
599 | if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i)) | ||
600 | return -1; | ||
601 | return 0; | ||
602 | } | ||
603 | |||
444 | static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) | 604 | static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) |
445 | { | 605 | { |
446 | struct dccp_sock *dp = dccp_sk(sk); | 606 | struct dccp_sock *dp = dccp_sk(sk); |
diff --git a/net/dccp/output.c b/net/dccp/output.c index d06945c7d3df..dc96ecfbe6e8 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c | |||
@@ -469,6 +469,10 @@ int dccp_connect(struct sock *sk) | |||
469 | struct sk_buff *skb; | 469 | struct sk_buff *skb; |
470 | struct inet_connection_sock *icsk = inet_csk(sk); | 470 | struct inet_connection_sock *icsk = inet_csk(sk); |
471 | 471 | ||
472 | /* do not connect if feature negotiation setup fails */ | ||
473 | if (dccp_feat_finalise_settings(dccp_sk(sk))) | ||
474 | return -EPROTO; | ||
475 | |||
472 | dccp_connect_init(sk); | 476 | dccp_connect_init(sk); |
473 | 477 | ||
474 | skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation); | 478 | skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation); |
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index b4b10cbd8880..46cb3490d48e 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -278,6 +278,9 @@ static inline int dccp_listen_start(struct sock *sk, int backlog) | |||
278 | struct dccp_sock *dp = dccp_sk(sk); | 278 | struct dccp_sock *dp = dccp_sk(sk); |
279 | 279 | ||
280 | dp->dccps_role = DCCP_ROLE_LISTEN; | 280 | dp->dccps_role = DCCP_ROLE_LISTEN; |
281 | /* do not start to listen if feature negotiation setup fails */ | ||
282 | if (dccp_feat_finalise_settings(dp)) | ||
283 | return -EPROTO; | ||
281 | return inet_csk_listen_start(sk, backlog); | 284 | return inet_csk_listen_start(sk, backlog); |
282 | } | 285 | } |
283 | 286 | ||