diff options
-rw-r--r-- | include/linux/dccp.h | 7 | ||||
-rw-r--r-- | net/dccp/feat.c | 72 | ||||
-rw-r--r-- | net/dccp/feat.h | 5 | ||||
-rw-r--r-- | net/dccp/proto.c | 53 |
4 files changed, 32 insertions, 105 deletions
diff --git a/include/linux/dccp.h b/include/linux/dccp.h index d3ac1bde60b4..6eaaca9b037a 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h | |||
@@ -193,13 +193,6 @@ enum dccp_feature_numbers { | |||
193 | DCCPF_MAX_CCID_SPECIFIC = 255, | 193 | DCCPF_MAX_CCID_SPECIFIC = 255, |
194 | }; | 194 | }; |
195 | 195 | ||
196 | /* this structure is argument to DCCP_SOCKOPT_CHANGE_X */ | ||
197 | struct dccp_so_feat { | ||
198 | __u8 dccpsf_feat; | ||
199 | __u8 __user *dccpsf_val; | ||
200 | __u8 dccpsf_len; | ||
201 | }; | ||
202 | |||
203 | /* DCCP socket options */ | 196 | /* DCCP socket options */ |
204 | #define DCCP_SOCKOPT_PACKET_SIZE 1 /* XXX deprecated, without effect */ | 197 | #define DCCP_SOCKOPT_PACKET_SIZE 1 /* XXX deprecated, without effect */ |
205 | #define DCCP_SOCKOPT_SERVICE 2 | 198 | #define DCCP_SOCKOPT_SERVICE 2 |
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 6852960bb0a9..44b10afd3fb6 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -393,53 +393,35 @@ static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local, | |||
393 | return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); | 393 | return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval); |
394 | } | 394 | } |
395 | 395 | ||
396 | int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 396 | /** |
397 | u8 *val, u8 len, gfp_t gfp) | 397 | * dccp_feat_register_sp - Register requests to change SP feature values |
398 | { | 398 | * @sk: client or listening socket |
399 | struct dccp_opt_pend *opt; | 399 | * @feat: one of %dccp_feature_numbers |
400 | 400 | * @is_local: whether the local (1) or remote (0) @feat is meant | |
401 | dccp_feat_debug(type, feature, *val); | 401 | * @list: array of preferred values, in descending order of preference |
402 | 402 | * @len: length of @list in bytes | |
403 | if (len > 3) { | 403 | */ |
404 | DCCP_WARN("invalid length %d\n", len); | 404 | int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
405 | u8 const *list, u8 len) | ||
406 | { /* any changes must be registered before establishing the connection */ | ||
407 | if (sk->sk_state != DCCP_CLOSED) | ||
408 | return -EISCONN; | ||
409 | if (dccp_feat_type(feat) != FEAT_SP) | ||
405 | return -EINVAL; | 410 | return -EINVAL; |
406 | } | 411 | return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local, |
407 | /* XXX add further sanity checks */ | 412 | 0, list, len); |
408 | |||
409 | /* check if that feature is already being negotiated */ | ||
410 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | ||
411 | /* ok we found a negotiation for this option already */ | ||
412 | if (opt->dccpop_feat == feature && opt->dccpop_type == type) { | ||
413 | dccp_pr_debug("Replacing old\n"); | ||
414 | /* replace */ | ||
415 | BUG_ON(opt->dccpop_val == NULL); | ||
416 | kfree(opt->dccpop_val); | ||
417 | opt->dccpop_val = val; | ||
418 | opt->dccpop_len = len; | ||
419 | opt->dccpop_conf = 0; | ||
420 | return 0; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | /* negotiation for a new feature */ | ||
425 | opt = kmalloc(sizeof(*opt), gfp); | ||
426 | if (opt == NULL) | ||
427 | return -ENOMEM; | ||
428 | |||
429 | opt->dccpop_type = type; | ||
430 | opt->dccpop_feat = feature; | ||
431 | opt->dccpop_len = len; | ||
432 | opt->dccpop_val = val; | ||
433 | opt->dccpop_conf = 0; | ||
434 | opt->dccpop_sc = NULL; | ||
435 | |||
436 | BUG_ON(opt->dccpop_val == NULL); | ||
437 | |||
438 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending); | ||
439 | return 0; | ||
440 | } | 413 | } |
441 | 414 | ||
442 | EXPORT_SYMBOL_GPL(dccp_feat_change); | 415 | /* Analogous to dccp_feat_register_sp(), but for non-negotiable values */ |
416 | int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val) | ||
417 | { | ||
418 | /* any changes must be registered before establishing the connection */ | ||
419 | if (sk->sk_state != DCCP_CLOSED) | ||
420 | return -EISCONN; | ||
421 | if (dccp_feat_type(feat) != FEAT_NN) | ||
422 | return -EINVAL; | ||
423 | return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val); | ||
424 | } | ||
443 | 425 | ||
444 | /* | 426 | /* |
445 | * Tracking features whose value depend on the choice of CCID | 427 | * Tracking features whose value depend on the choice of CCID |
@@ -1137,7 +1119,7 @@ int dccp_feat_init(struct sock *sk) | |||
1137 | 1119 | ||
1138 | /* Ack ratio */ | 1120 | /* Ack ratio */ |
1139 | rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, | 1121 | rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, |
1140 | dmsk->dccpms_ack_ratio); | 1122 | dp->dccps_l_ack_ratio); |
1141 | out: | 1123 | out: |
1142 | return rc; | 1124 | return rc; |
1143 | } | 1125 | } |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 9eefdb43783e..2c92bd18e5f4 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
@@ -110,8 +110,9 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) | |||
110 | #define dccp_feat_debug(type, feat, val) | 110 | #define dccp_feat_debug(type, feat, val) |
111 | #endif /* CONFIG_IP_DCCP_DEBUG */ | 111 | #endif /* CONFIG_IP_DCCP_DEBUG */ |
112 | 112 | ||
113 | extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, | 113 | extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, |
114 | u8 *val, u8 len, gfp_t gfp); | 114 | u8 const *list, u8 len); |
115 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); | ||
115 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, | 116 | extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, |
116 | u8 *val, u8 len); | 117 | u8 *val, u8 len); |
117 | extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | 118 | 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 46cb3490d48e..108d56bd25c5 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) |