diff options
Diffstat (limited to 'net/dccp')
-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 f79fb5e33f5e..4c4144147325 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -400,6 +400,166 @@ int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | |||
400 | 400 | ||
401 | EXPORT_SYMBOL_GPL(dccp_feat_change); | 401 | EXPORT_SYMBOL_GPL(dccp_feat_change); |
402 | 402 | ||
403 | /* | ||
404 | * Tracking features whose value depend on the choice of CCID | ||
405 | * | ||
406 | * This is designed with an extension in mind so that a list walk could be done | ||
407 | * before activating any features. However, the existing framework was found to | ||
408 | * work satisfactorily up until now, the automatic verification is left open. | ||
409 | * When adding new CCIDs, add a corresponding dependency table here. | ||
410 | */ | ||
411 | static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local) | ||
412 | { | ||
413 | static const struct ccid_dependency ccid2_dependencies[2][2] = { | ||
414 | /* | ||
415 | * CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX | ||
416 | * feature and Send Ack Vector is an RX feature, `is_local' | ||
417 | * needs to be reversed. | ||
418 | */ | ||
419 | { /* Dependencies of the receiver-side (remote) CCID2 */ | ||
420 | { | ||
421 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
422 | .is_local = true, | ||
423 | .is_mandatory = true, | ||
424 | .val = 1 | ||
425 | }, | ||
426 | { 0, 0, 0, 0 } | ||
427 | }, | ||
428 | { /* Dependencies of the sender-side (local) CCID2 */ | ||
429 | { | ||
430 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
431 | .is_local = false, | ||
432 | .is_mandatory = true, | ||
433 | .val = 1 | ||
434 | }, | ||
435 | { 0, 0, 0, 0 } | ||
436 | } | ||
437 | }; | ||
438 | static const struct ccid_dependency ccid3_dependencies[2][5] = { | ||
439 | { /* | ||
440 | * Dependencies of the receiver-side CCID3 | ||
441 | */ | ||
442 | { /* locally disable Ack Vectors */ | ||
443 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
444 | .is_local = true, | ||
445 | .is_mandatory = false, | ||
446 | .val = 0 | ||
447 | }, | ||
448 | { /* see below why Send Loss Event Rate is on */ | ||
449 | .dependent_feat = DCCPF_SEND_LEV_RATE, | ||
450 | .is_local = true, | ||
451 | .is_mandatory = true, | ||
452 | .val = 1 | ||
453 | }, | ||
454 | { /* NDP Count is needed as per RFC 4342, 6.1.1 */ | ||
455 | .dependent_feat = DCCPF_SEND_NDP_COUNT, | ||
456 | .is_local = false, | ||
457 | .is_mandatory = true, | ||
458 | .val = 1 | ||
459 | }, | ||
460 | { 0, 0, 0, 0 }, | ||
461 | }, | ||
462 | { /* | ||
463 | * CCID3 at the TX side: we request that the HC-receiver | ||
464 | * will not send Ack Vectors (they will be ignored, so | ||
465 | * Mandatory is not set); we enable Send Loss Event Rate | ||
466 | * (Mandatory since the implementation does not support | ||
467 | * the Loss Intervals option of RFC 4342, 8.6). | ||
468 | * The last two options are for peer's information only. | ||
469 | */ | ||
470 | { | ||
471 | .dependent_feat = DCCPF_SEND_ACK_VECTOR, | ||
472 | .is_local = false, | ||
473 | .is_mandatory = false, | ||
474 | .val = 0 | ||
475 | }, | ||
476 | { | ||
477 | .dependent_feat = DCCPF_SEND_LEV_RATE, | ||
478 | .is_local = false, | ||
479 | .is_mandatory = true, | ||
480 | .val = 1 | ||
481 | }, | ||
482 | { /* this CCID does not support Ack Ratio */ | ||
483 | .dependent_feat = DCCPF_ACK_RATIO, | ||
484 | .is_local = true, | ||
485 | .is_mandatory = false, | ||
486 | .val = 0 | ||
487 | }, | ||
488 | { /* tell receiver we are sending NDP counts */ | ||
489 | .dependent_feat = DCCPF_SEND_NDP_COUNT, | ||
490 | .is_local = true, | ||
491 | .is_mandatory = false, | ||
492 | .val = 1 | ||
493 | }, | ||
494 | { 0, 0, 0, 0 } | ||
495 | } | ||
496 | }; | ||
497 | switch (ccid) { | ||
498 | case DCCPC_CCID2: | ||
499 | return ccid2_dependencies[is_local]; | ||
500 | case DCCPC_CCID3: | ||
501 | return ccid3_dependencies[is_local]; | ||
502 | default: | ||
503 | return NULL; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID | ||
509 | * @fn: feature-negotiation list to update | ||
510 | * @id: CCID number to track | ||
511 | * @is_local: whether TX CCID (1) or RX CCID (0) is meant | ||
512 | * This function needs to be called after registering all other features. | ||
513 | */ | ||
514 | static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local) | ||
515 | { | ||
516 | const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local); | ||
517 | int i, rc = (table == NULL); | ||
518 | |||
519 | for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++) | ||
520 | if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP) | ||
521 | rc = __feat_register_sp(fn, table[i].dependent_feat, | ||
522 | table[i].is_local, | ||
523 | table[i].is_mandatory, | ||
524 | &table[i].val, 1); | ||
525 | else | ||
526 | rc = __feat_register_nn(fn, table[i].dependent_feat, | ||
527 | table[i].is_mandatory, | ||
528 | table[i].val); | ||
529 | return rc; | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * dccp_feat_finalise_settings - Finalise settings before starting negotiation | ||
534 | * @dp: client or listening socket (settings will be inherited) | ||
535 | * This is called after all registrations (socket initialisation, sysctls, and | ||
536 | * sockopt calls), and before sending the first packet containing Change options | ||
537 | * (ie. client-Request or server-Response), to ensure internal consistency. | ||
538 | */ | ||
539 | int dccp_feat_finalise_settings(struct dccp_sock *dp) | ||
540 | { | ||
541 | struct list_head *fn = &dp->dccps_featneg; | ||
542 | struct dccp_feat_entry *entry; | ||
543 | int i = 2, ccids[2] = { -1, -1 }; | ||
544 | |||
545 | /* | ||
546 | * Propagating CCIDs: | ||
547 | * 1) not useful to propagate CCID settings if this host advertises more | ||
548 | * than one CCID: the choice of CCID may still change - if this is | ||
549 | * the client, or if this is the server and the client sends | ||
550 | * singleton CCID values. | ||
551 | * 2) since is that propagate_ccid changes the list, we defer changing | ||
552 | * the sorted list until after the traversal. | ||
553 | */ | ||
554 | list_for_each_entry(entry, fn, node) | ||
555 | if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1) | ||
556 | ccids[entry->is_local] = entry->val.sp.vec[0]; | ||
557 | while (i--) | ||
558 | if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i)) | ||
559 | return -1; | ||
560 | return 0; | ||
561 | } | ||
562 | |||
403 | static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) | 563 | static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) |
404 | { | 564 | { |
405 | struct dccp_sock *dp = dccp_sk(sk); | 565 | struct dccp_sock *dp = dccp_sk(sk); |
diff --git a/net/dccp/output.c b/net/dccp/output.c index 809d803d5006..92f3f6f2ef51 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 | ||