aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp
diff options
context:
space:
mode:
Diffstat (limited to 'net/dccp')
-rw-r--r--net/dccp/feat.c72
-rw-r--r--net/dccp/feat.h5
-rw-r--r--net/dccp/proto.c53
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
367int 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); 375int 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
413EXPORT_SYMBOL_GPL(dccp_feat_change); 386/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */
387int 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);
1112out: 1094out:
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
114extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature, 114extern 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);
116extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
116extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, 117extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature,
117 u8 *val, u8 len); 118 u8 *val, u8 len);
118extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, 119extern 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 */
474static 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
503out:
504 return rc;
505
506out_free_val:
507 kfree(val);
508 goto out;
509}
510
511static int do_dccp_setsockopt(struct sock *sk, int level, int optname, 473static 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)