diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/dccp/feat.c | 72 | ||||
| -rw-r--r-- | net/dccp/feat.h | 5 | ||||
| -rw-r--r-- | net/dccp/proto.c | 53 |
3 files changed, 32 insertions, 98 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 4f86a48723f6..47ce9abaf72b 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
| @@ -364,53 +364,35 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, | |||
| 364 | return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); | 364 | return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); |
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 367 | /** |
| 368 | u8 *val, u8 len, gfp_t gfp) | 368 | * dccp_feat_register_sp - Register requests to change SP feature values |
| 369 | { | 369 | * @sk: client or listening socket |
| 370 | struct dccp_opt_pend *opt; | 370 | * @feat: one of %dccp_feature_numbers |
| 371 | 371 | * @is_local: whether the local (1) or remote (0) @feat is meant | |
| 372 | dccp_feat_debug(type, feature, *val); | 372 | * @list: array of preferred values, in descending order of preference |
| 373 | 373 | * @len: length of @list in bytes | |
| 374 | if (len > 3) { | 374 | */ |
| 375 | DCCP_WARN("invalid length %d\n", len); | 375 | int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
| 376 | u8 const *list, u8 len) | ||
| 377 | { /* any changes must be registered before establishing the connection */ | ||
| 378 | if (sk->sk_state != DCCP_CLOSED) | ||
| 379 | return -EISCONN; | ||
| 380 | if (dccp_feat_type(feat) != FEAT_SP) | ||
| 376 | return -EINVAL; | 381 | return -EINVAL; |
| 377 | } | 382 | return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local, |
| 378 | /* XXX add further sanity checks */ | 383 | 0, list, len); |
| 379 | |||
| 380 | /* check if that feature is already being negotiated */ | ||
| 381 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | ||
| 382 | /* ok we found a negotiation for this option already */ | ||
| 383 | if (opt->dccpop_feat == feature && opt->dccpop_type == type) { | ||
| 384 | dccp_pr_debug("Replacing old\n"); | ||
| 385 | /* replace */ | ||
| 386 | BUG_ON(opt->dccpop_val == NULL); | ||
| 387 | kfree(opt->dccpop_val); | ||
| 388 | opt->dccpop_val = val; | ||
| 389 | opt->dccpop_len = len; | ||
| 390 | opt->dccpop_conf = 0; | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | /* negotiation for a new feature */ | ||
| 396 | opt = kmalloc(sizeof(*opt), gfp); | ||
| 397 | if (opt == NULL) | ||
| 398 | return -ENOMEM; | ||
| 399 | |||
| 400 | opt->dccpop_type = type; | ||
| 401 | opt->dccpop_feat = feature; | ||
| 402 | opt->dccpop_len = len; | ||
| 403 | opt->dccpop_val = val; | ||
| 404 | opt->dccpop_conf = 0; | ||
| 405 | opt->dccpop_sc = NULL; | ||
| 406 | |||
| 407 | BUG_ON(opt->dccpop_val == NULL); | ||
| 408 | |||
| 409 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); | ||
| 410 | return 0; | ||
| 411 | } | 384 | } |
| 412 | 385 | ||
| 413 | EXPORT_SYMBOL_GPL(dccp_feat_change); | 386 | /* Analogous to dccp_feat_register_sp(), but for non-negotiable values */ |
| 387 | int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val) | ||
| 388 | { | ||
| 389 | /* any changes must be registered before establishing the connection */ | ||
| 390 | if (sk->sk_state != DCCP_CLOSED) | ||
| 391 | return -EISCONN; | ||
| 392 | if (dccp_feat_type(feat) != FEAT_NN) | ||
| 393 | return -EINVAL; | ||
| 394 | return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val); | ||
| 395 | } | ||
| 414 | 396 | ||
| 415 | /* | 397 | /* |
| 416 | * Tracking features whose value depend on the choice of CCID | 398 | * Tracking features whose value depend on the choice of CCID |
| @@ -1108,7 +1090,7 @@ int dccp_feat_init(struct sock *sk) | |||
| 1108 | 1090 | ||
| 1109 | /* Ack ratio */ | 1091 | /* Ack ratio */ |
| 1110 | rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, | 1092 | rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, |
| 1111 | dmsk->dccpms_ack_ratio); | 1093 | dp->dccps_l_ack_ratio); |
| 1112 | out: | 1094 | out: |
| 1113 | return rc; | 1095 | return rc; |
| 1114 | } | 1096 | } |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 232c653e69c5..4d172822df17 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
| @@ -111,8 +111,9 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) | |||
| 111 | #define dccp_feat_debug(type, feat, val) | 111 | #define dccp_feat_debug(type, feat, val) |
| 112 | #endif /* CONFIG_IP_DCCP_DEBUG */ | 112 | #endif /* CONFIG_IP_DCCP_DEBUG */ |
| 113 | 113 | ||
| 114 | extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 114 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
| 115 | u8 *val, u8 len, gfp_t gfp); | 115 | u8 const *list, u8 len); |
| 116 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); | ||
| 116 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, | 117 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, |
| 117 | u8 *val, u8 len); | 118 | u8 *val, u8 len); |
| 118 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 119 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, |
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 1117d4d8c8f1..3090fc40eebd 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
| @@ -470,44 +470,6 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, | |||
| 470 | return 0; | 470 | return 0; |
| 471 | } | 471 | } |
| 472 | 472 | ||
| 473 | /* byte 1 is feature. the rest is the preference list */ | ||
| 474 | static int dccp_setsockopt_change(struct sock *sk, int type, | ||
| 475 | struct dccp_so_feat __user *optval) | ||
| 476 | { | ||
| 477 | struct dccp_so_feat opt; | ||
| 478 | u8 *val; | ||
| 479 | int rc; | ||
| 480 | |||
| 481 | if (copy_from_user(&opt, optval, sizeof(opt))) | ||
| 482 | return -EFAULT; | ||
| 483 | /* | ||
| 484 | * rfc4340: 6.1. Change Options | ||
| 485 | */ | ||
| 486 | if (opt.dccpsf_len < 1) | ||
| 487 | return -EINVAL; | ||
| 488 | |||
| 489 | val = kmalloc(opt.dccpsf_len, GFP_KERNEL); | ||
| 490 | if (!val) | ||
| 491 | return -ENOMEM; | ||
| 492 | |||
| 493 | if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { | ||
| 494 | rc = -EFAULT; | ||
| 495 | goto out_free_val; | ||
| 496 | } | ||
| 497 | |||
| 498 | rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, | ||
| 499 | val, opt.dccpsf_len, GFP_KERNEL); | ||
| 500 | if (rc) | ||
| 501 | goto out_free_val; | ||
| 502 | |||
| 503 | out: | ||
| 504 | return rc; | ||
| 505 | |||
| 506 | out_free_val: | ||
| 507 | kfree(val); | ||
| 508 | goto out; | ||
| 509 | } | ||
| 510 | |||
| 511 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | 473 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
| 512 | char __user *optval, int optlen) | 474 | char __user *optval, int optlen) |
| 513 | { | 475 | { |
| @@ -530,20 +492,9 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
| 530 | err = 0; | 492 | err = 0; |
| 531 | break; | 493 | break; |
| 532 | case DCCP_SOCKOPT_CHANGE_L: | 494 | case DCCP_SOCKOPT_CHANGE_L: |
| 533 | if (optlen != sizeof(struct dccp_so_feat)) | ||
| 534 | err = -EINVAL; | ||
| 535 | else | ||
| 536 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, | ||
| 537 | (struct dccp_so_feat __user *) | ||
| 538 | optval); | ||
| 539 | break; | ||
| 540 | case DCCP_SOCKOPT_CHANGE_R: | 495 | case DCCP_SOCKOPT_CHANGE_R: |
| 541 | if (optlen != sizeof(struct dccp_so_feat)) | 496 | DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); |
| 542 | err = -EINVAL; | 497 | err = 0; |
| 543 | else | ||
| 544 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, | ||
| 545 | (struct dccp_so_feat __user *) | ||
| 546 | optval); | ||
| 547 | break; | 498 | break; |
| 548 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: | 499 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: |
| 549 | if (dp->dccps_role != DCCP_ROLE_SERVER) | 500 | if (dp->dccps_role != DCCP_ROLE_SERVER) |
