diff options
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r-- | net/dccp/proto.c | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 3090fc40eebd..c6b4362bb1d7 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -470,6 +470,42 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, | |||
470 | return 0; | 470 | return 0; |
471 | } | 471 | } |
472 | 472 | ||
473 | static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) | ||
474 | { | ||
475 | u8 *list, len; | ||
476 | int i, rc; | ||
477 | |||
478 | if (cscov < 0 || cscov > 15) | ||
479 | return -EINVAL; | ||
480 | /* | ||
481 | * Populate a list of permissible values, in the range cscov...15. This | ||
482 | * is necessary since feature negotiation of single values only works if | ||
483 | * both sides incidentally choose the same value. Since the list starts | ||
484 | * lowest-value first, negotiation will pick the smallest shared value. | ||
485 | */ | ||
486 | if (cscov == 0) | ||
487 | return 0; | ||
488 | len = 16 - cscov; | ||
489 | |||
490 | list = kmalloc(len, GFP_KERNEL); | ||
491 | if (list == NULL) | ||
492 | return -ENOBUFS; | ||
493 | |||
494 | for (i = 0; i < len; i++) | ||
495 | list[i] = cscov++; | ||
496 | |||
497 | rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); | ||
498 | |||
499 | if (rc == 0) { | ||
500 | if (rx) | ||
501 | dccp_sk(sk)->dccps_pcrlen = cscov; | ||
502 | else | ||
503 | dccp_sk(sk)->dccps_pcslen = cscov; | ||
504 | } | ||
505 | kfree(list); | ||
506 | return rc; | ||
507 | } | ||
508 | |||
473 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | 509 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
474 | char __user *optval, int optlen) | 510 | char __user *optval, int optlen) |
475 | { | 511 | { |
@@ -502,20 +538,11 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
502 | else | 538 | else |
503 | dp->dccps_server_timewait = (val != 0); | 539 | dp->dccps_server_timewait = (val != 0); |
504 | break; | 540 | break; |
505 | case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */ | 541 | case DCCP_SOCKOPT_SEND_CSCOV: |
506 | if (val < 0 || val > 15) | 542 | err = dccp_setsockopt_cscov(sk, val, false); |
507 | err = -EINVAL; | ||
508 | else | ||
509 | dp->dccps_pcslen = val; | ||
510 | break; | 543 | break; |
511 | case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */ | 544 | case DCCP_SOCKOPT_RECV_CSCOV: |
512 | if (val < 0 || val > 15) | 545 | err = dccp_setsockopt_cscov(sk, val, true); |
513 | err = -EINVAL; | ||
514 | else { | ||
515 | dp->dccps_pcrlen = val; | ||
516 | /* FIXME: add feature negotiation, | ||
517 | * ChangeL(MinimumChecksumCoverage, val) */ | ||
518 | } | ||
519 | break; | 546 | break; |
520 | default: | 547 | default: |
521 | err = -ENOPROTOOPT; | 548 | err = -ENOPROTOOPT; |