diff options
Diffstat (limited to 'net/dccp')
-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) |