diff options
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r-- | net/dccp/proto.c | 224 |
1 files changed, 109 insertions, 115 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index d0bd34819761..d5c2bacb713c 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -40,16 +40,10 @@ DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; | |||
40 | 40 | ||
41 | EXPORT_SYMBOL_GPL(dccp_statistics); | 41 | EXPORT_SYMBOL_GPL(dccp_statistics); |
42 | 42 | ||
43 | atomic_t dccp_orphan_count = ATOMIC_INIT(0); | 43 | struct percpu_counter dccp_orphan_count; |
44 | |||
45 | EXPORT_SYMBOL_GPL(dccp_orphan_count); | 44 | EXPORT_SYMBOL_GPL(dccp_orphan_count); |
46 | 45 | ||
47 | struct inet_hashinfo __cacheline_aligned dccp_hashinfo = { | 46 | struct inet_hashinfo dccp_hashinfo; |
48 | .lhash_lock = RW_LOCK_UNLOCKED, | ||
49 | .lhash_users = ATOMIC_INIT(0), | ||
50 | .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait), | ||
51 | }; | ||
52 | |||
53 | EXPORT_SYMBOL_GPL(dccp_hashinfo); | 47 | EXPORT_SYMBOL_GPL(dccp_hashinfo); |
54 | 48 | ||
55 | /* the maximum queue length for tx in packets. 0 is no limit */ | 49 | /* the maximum queue length for tx in packets. 0 is no limit */ |
@@ -67,6 +61,9 @@ void dccp_set_state(struct sock *sk, const int state) | |||
67 | case DCCP_OPEN: | 61 | case DCCP_OPEN: |
68 | if (oldstate != DCCP_OPEN) | 62 | if (oldstate != DCCP_OPEN) |
69 | DCCP_INC_STATS(DCCP_MIB_CURRESTAB); | 63 | DCCP_INC_STATS(DCCP_MIB_CURRESTAB); |
64 | /* Client retransmits all Confirm options until entering OPEN */ | ||
65 | if (oldstate == DCCP_PARTOPEN) | ||
66 | dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg); | ||
70 | break; | 67 | break; |
71 | 68 | ||
72 | case DCCP_CLOSED: | 69 | case DCCP_CLOSED: |
@@ -175,7 +172,6 @@ EXPORT_SYMBOL_GPL(dccp_state_name); | |||
175 | int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) | 172 | int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) |
176 | { | 173 | { |
177 | struct dccp_sock *dp = dccp_sk(sk); | 174 | struct dccp_sock *dp = dccp_sk(sk); |
178 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
179 | struct inet_connection_sock *icsk = inet_csk(sk); | 175 | struct inet_connection_sock *icsk = inet_csk(sk); |
180 | 176 | ||
181 | dccp_minisock_init(&dp->dccps_minisock); | 177 | dccp_minisock_init(&dp->dccps_minisock); |
@@ -193,45 +189,10 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) | |||
193 | 189 | ||
194 | dccp_init_xmit_timers(sk); | 190 | dccp_init_xmit_timers(sk); |
195 | 191 | ||
196 | /* | 192 | INIT_LIST_HEAD(&dp->dccps_featneg); |
197 | * FIXME: We're hardcoding the CCID, and doing this at this point makes | 193 | /* control socket doesn't need feat nego */ |
198 | * the listening (master) sock get CCID control blocks, which is not | 194 | if (likely(ctl_sock_initialized)) |
199 | * necessary, but for now, to not mess with the test userspace apps, | 195 | return dccp_feat_init(sk); |
200 | * lets leave it here, later the real solution is to do this in a | ||
201 | * setsockopt(CCIDs-I-want/accept). -acme | ||
202 | */ | ||
203 | if (likely(ctl_sock_initialized)) { | ||
204 | int rc = dccp_feat_init(dmsk); | ||
205 | |||
206 | if (rc) | ||
207 | return rc; | ||
208 | |||
209 | if (dmsk->dccpms_send_ack_vector) { | ||
210 | dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL); | ||
211 | if (dp->dccps_hc_rx_ackvec == NULL) | ||
212 | return -ENOMEM; | ||
213 | } | ||
214 | dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid, | ||
215 | sk, GFP_KERNEL); | ||
216 | dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid, | ||
217 | sk, GFP_KERNEL); | ||
218 | if (unlikely(dp->dccps_hc_rx_ccid == NULL || | ||
219 | dp->dccps_hc_tx_ccid == NULL)) { | ||
220 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
221 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
222 | if (dmsk->dccpms_send_ack_vector) { | ||
223 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | ||
224 | dp->dccps_hc_rx_ackvec = NULL; | ||
225 | } | ||
226 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | ||
227 | return -ENOMEM; | ||
228 | } | ||
229 | } else { | ||
230 | /* control socket doesn't need feat nego */ | ||
231 | INIT_LIST_HEAD(&dmsk->dccpms_pending); | ||
232 | INIT_LIST_HEAD(&dmsk->dccpms_conf); | ||
233 | } | ||
234 | |||
235 | return 0; | 196 | return 0; |
236 | } | 197 | } |
237 | 198 | ||
@@ -240,7 +201,6 @@ EXPORT_SYMBOL_GPL(dccp_init_sock); | |||
240 | void dccp_destroy_sock(struct sock *sk) | 201 | void dccp_destroy_sock(struct sock *sk) |
241 | { | 202 | { |
242 | struct dccp_sock *dp = dccp_sk(sk); | 203 | struct dccp_sock *dp = dccp_sk(sk); |
243 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
244 | 204 | ||
245 | /* | 205 | /* |
246 | * DCCP doesn't use sk_write_queue, just sk_send_head | 206 | * DCCP doesn't use sk_write_queue, just sk_send_head |
@@ -258,7 +218,7 @@ void dccp_destroy_sock(struct sock *sk) | |||
258 | kfree(dp->dccps_service_list); | 218 | kfree(dp->dccps_service_list); |
259 | dp->dccps_service_list = NULL; | 219 | dp->dccps_service_list = NULL; |
260 | 220 | ||
261 | if (dmsk->dccpms_send_ack_vector) { | 221 | if (dp->dccps_hc_rx_ackvec != NULL) { |
262 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | 222 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); |
263 | dp->dccps_hc_rx_ackvec = NULL; | 223 | dp->dccps_hc_rx_ackvec = NULL; |
264 | } | 224 | } |
@@ -267,7 +227,7 @@ void dccp_destroy_sock(struct sock *sk) | |||
267 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | 227 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; |
268 | 228 | ||
269 | /* clean up feature negotiation state */ | 229 | /* clean up feature negotiation state */ |
270 | dccp_feat_clean(dmsk); | 230 | dccp_feat_list_purge(&dp->dccps_featneg); |
271 | } | 231 | } |
272 | 232 | ||
273 | EXPORT_SYMBOL_GPL(dccp_destroy_sock); | 233 | EXPORT_SYMBOL_GPL(dccp_destroy_sock); |
@@ -277,6 +237,9 @@ static inline int dccp_listen_start(struct sock *sk, int backlog) | |||
277 | struct dccp_sock *dp = dccp_sk(sk); | 237 | struct dccp_sock *dp = dccp_sk(sk); |
278 | 238 | ||
279 | dp->dccps_role = DCCP_ROLE_LISTEN; | 239 | dp->dccps_role = DCCP_ROLE_LISTEN; |
240 | /* do not start to listen if feature negotiation setup fails */ | ||
241 | if (dccp_feat_finalise_settings(dp)) | ||
242 | return -EPROTO; | ||
280 | return inet_csk_listen_start(sk, backlog); | 243 | return inet_csk_listen_start(sk, backlog); |
281 | } | 244 | } |
282 | 245 | ||
@@ -466,42 +429,70 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, | |||
466 | return 0; | 429 | return 0; |
467 | } | 430 | } |
468 | 431 | ||
469 | /* byte 1 is feature. the rest is the preference list */ | 432 | static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx) |
470 | static int dccp_setsockopt_change(struct sock *sk, int type, | ||
471 | struct dccp_so_feat __user *optval) | ||
472 | { | 433 | { |
473 | struct dccp_so_feat opt; | 434 | u8 *list, len; |
474 | u8 *val; | 435 | int i, rc; |
475 | int rc; | ||
476 | 436 | ||
477 | if (copy_from_user(&opt, optval, sizeof(opt))) | 437 | if (cscov < 0 || cscov > 15) |
478 | return -EFAULT; | 438 | return -EINVAL; |
479 | /* | 439 | /* |
480 | * rfc4340: 6.1. Change Options | 440 | * Populate a list of permissible values, in the range cscov...15. This |
441 | * is necessary since feature negotiation of single values only works if | ||
442 | * both sides incidentally choose the same value. Since the list starts | ||
443 | * lowest-value first, negotiation will pick the smallest shared value. | ||
481 | */ | 444 | */ |
482 | if (opt.dccpsf_len < 1) | 445 | if (cscov == 0) |
446 | return 0; | ||
447 | len = 16 - cscov; | ||
448 | |||
449 | list = kmalloc(len, GFP_KERNEL); | ||
450 | if (list == NULL) | ||
451 | return -ENOBUFS; | ||
452 | |||
453 | for (i = 0; i < len; i++) | ||
454 | list[i] = cscov++; | ||
455 | |||
456 | rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); | ||
457 | |||
458 | if (rc == 0) { | ||
459 | if (rx) | ||
460 | dccp_sk(sk)->dccps_pcrlen = cscov; | ||
461 | else | ||
462 | dccp_sk(sk)->dccps_pcslen = cscov; | ||
463 | } | ||
464 | kfree(list); | ||
465 | return rc; | ||
466 | } | ||
467 | |||
468 | static int dccp_setsockopt_ccid(struct sock *sk, int type, | ||
469 | char __user *optval, int optlen) | ||
470 | { | ||
471 | u8 *val; | ||
472 | int rc = 0; | ||
473 | |||
474 | if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) | ||
483 | return -EINVAL; | 475 | return -EINVAL; |
484 | 476 | ||
485 | val = kmalloc(opt.dccpsf_len, GFP_KERNEL); | 477 | val = kmalloc(optlen, GFP_KERNEL); |
486 | if (!val) | 478 | if (val == NULL) |
487 | return -ENOMEM; | 479 | return -ENOMEM; |
488 | 480 | ||
489 | if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { | 481 | if (copy_from_user(val, optval, optlen)) { |
490 | rc = -EFAULT; | 482 | kfree(val); |
491 | goto out_free_val; | 483 | return -EFAULT; |
492 | } | 484 | } |
493 | 485 | ||
494 | rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, | 486 | lock_sock(sk); |
495 | val, opt.dccpsf_len, GFP_KERNEL); | 487 | if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) |
496 | if (rc) | 488 | rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); |
497 | goto out_free_val; | ||
498 | 489 | ||
499 | out: | 490 | if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) |
500 | return rc; | 491 | rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); |
492 | release_sock(sk); | ||
501 | 493 | ||
502 | out_free_val: | ||
503 | kfree(val); | 494 | kfree(val); |
504 | goto out; | 495 | return rc; |
505 | } | 496 | } |
506 | 497 | ||
507 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | 498 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
@@ -510,7 +501,21 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
510 | struct dccp_sock *dp = dccp_sk(sk); | 501 | struct dccp_sock *dp = dccp_sk(sk); |
511 | int val, err = 0; | 502 | int val, err = 0; |
512 | 503 | ||
513 | if (optlen < sizeof(int)) | 504 | switch (optname) { |
505 | case DCCP_SOCKOPT_PACKET_SIZE: | ||
506 | DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); | ||
507 | return 0; | ||
508 | case DCCP_SOCKOPT_CHANGE_L: | ||
509 | case DCCP_SOCKOPT_CHANGE_R: | ||
510 | DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); | ||
511 | return 0; | ||
512 | case DCCP_SOCKOPT_CCID: | ||
513 | case DCCP_SOCKOPT_RX_CCID: | ||
514 | case DCCP_SOCKOPT_TX_CCID: | ||
515 | return dccp_setsockopt_ccid(sk, optname, optval, optlen); | ||
516 | } | ||
517 | |||
518 | if (optlen < (int)sizeof(int)) | ||
514 | return -EINVAL; | 519 | return -EINVAL; |
515 | 520 | ||
516 | if (get_user(val, (int __user *)optval)) | 521 | if (get_user(val, (int __user *)optval)) |
@@ -521,53 +526,24 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
521 | 526 | ||
522 | lock_sock(sk); | 527 | lock_sock(sk); |
523 | switch (optname) { | 528 | switch (optname) { |
524 | case DCCP_SOCKOPT_PACKET_SIZE: | ||
525 | DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); | ||
526 | err = 0; | ||
527 | break; | ||
528 | case DCCP_SOCKOPT_CHANGE_L: | ||
529 | if (optlen != sizeof(struct dccp_so_feat)) | ||
530 | err = -EINVAL; | ||
531 | else | ||
532 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, | ||
533 | (struct dccp_so_feat __user *) | ||
534 | optval); | ||
535 | break; | ||
536 | case DCCP_SOCKOPT_CHANGE_R: | ||
537 | if (optlen != sizeof(struct dccp_so_feat)) | ||
538 | err = -EINVAL; | ||
539 | else | ||
540 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, | ||
541 | (struct dccp_so_feat __user *) | ||
542 | optval); | ||
543 | break; | ||
544 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: | 529 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: |
545 | if (dp->dccps_role != DCCP_ROLE_SERVER) | 530 | if (dp->dccps_role != DCCP_ROLE_SERVER) |
546 | err = -EOPNOTSUPP; | 531 | err = -EOPNOTSUPP; |
547 | else | 532 | else |
548 | dp->dccps_server_timewait = (val != 0); | 533 | dp->dccps_server_timewait = (val != 0); |
549 | break; | 534 | break; |
550 | case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */ | 535 | case DCCP_SOCKOPT_SEND_CSCOV: |
551 | if (val < 0 || val > 15) | 536 | err = dccp_setsockopt_cscov(sk, val, false); |
552 | err = -EINVAL; | ||
553 | else | ||
554 | dp->dccps_pcslen = val; | ||
555 | break; | 537 | break; |
556 | case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */ | 538 | case DCCP_SOCKOPT_RECV_CSCOV: |
557 | if (val < 0 || val > 15) | 539 | err = dccp_setsockopt_cscov(sk, val, true); |
558 | err = -EINVAL; | ||
559 | else { | ||
560 | dp->dccps_pcrlen = val; | ||
561 | /* FIXME: add feature negotiation, | ||
562 | * ChangeL(MinimumChecksumCoverage, val) */ | ||
563 | } | ||
564 | break; | 540 | break; |
565 | default: | 541 | default: |
566 | err = -ENOPROTOOPT; | 542 | err = -ENOPROTOOPT; |
567 | break; | 543 | break; |
568 | } | 544 | } |
569 | |||
570 | release_sock(sk); | 545 | release_sock(sk); |
546 | |||
571 | return err; | 547 | return err; |
572 | } | 548 | } |
573 | 549 | ||
@@ -648,6 +624,18 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, | |||
648 | case DCCP_SOCKOPT_GET_CUR_MPS: | 624 | case DCCP_SOCKOPT_GET_CUR_MPS: |
649 | val = dp->dccps_mss_cache; | 625 | val = dp->dccps_mss_cache; |
650 | break; | 626 | break; |
627 | case DCCP_SOCKOPT_AVAILABLE_CCIDS: | ||
628 | return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); | ||
629 | case DCCP_SOCKOPT_TX_CCID: | ||
630 | val = ccid_get_current_tx_ccid(dp); | ||
631 | if (val < 0) | ||
632 | return -ENOPROTOOPT; | ||
633 | break; | ||
634 | case DCCP_SOCKOPT_RX_CCID: | ||
635 | val = ccid_get_current_rx_ccid(dp); | ||
636 | if (val < 0) | ||
637 | return -ENOPROTOOPT; | ||
638 | break; | ||
651 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: | 639 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: |
652 | val = dp->dccps_server_timewait; | 640 | val = dp->dccps_server_timewait; |
653 | break; | 641 | break; |
@@ -976,7 +964,7 @@ adjudge_to_death: | |||
976 | state = sk->sk_state; | 964 | state = sk->sk_state; |
977 | sock_hold(sk); | 965 | sock_hold(sk); |
978 | sock_orphan(sk); | 966 | sock_orphan(sk); |
979 | atomic_inc(sk->sk_prot->orphan_count); | 967 | percpu_counter_inc(sk->sk_prot->orphan_count); |
980 | 968 | ||
981 | /* | 969 | /* |
982 | * It is the last release_sock in its life. It will remove backlog. | 970 | * It is the last release_sock in its life. It will remove backlog. |
@@ -1040,17 +1028,21 @@ static int __init dccp_init(void) | |||
1040 | { | 1028 | { |
1041 | unsigned long goal; | 1029 | unsigned long goal; |
1042 | int ehash_order, bhash_order, i; | 1030 | int ehash_order, bhash_order, i; |
1043 | int rc = -ENOBUFS; | 1031 | int rc; |
1044 | 1032 | ||
1045 | BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > | 1033 | BUILD_BUG_ON(sizeof(struct dccp_skb_cb) > |
1046 | FIELD_SIZEOF(struct sk_buff, cb)); | 1034 | FIELD_SIZEOF(struct sk_buff, cb)); |
1047 | 1035 | rc = percpu_counter_init(&dccp_orphan_count, 0); | |
1036 | if (rc) | ||
1037 | goto out; | ||
1038 | rc = -ENOBUFS; | ||
1039 | inet_hashinfo_init(&dccp_hashinfo); | ||
1048 | dccp_hashinfo.bind_bucket_cachep = | 1040 | dccp_hashinfo.bind_bucket_cachep = |
1049 | kmem_cache_create("dccp_bind_bucket", | 1041 | kmem_cache_create("dccp_bind_bucket", |
1050 | sizeof(struct inet_bind_bucket), 0, | 1042 | sizeof(struct inet_bind_bucket), 0, |
1051 | SLAB_HWCACHE_ALIGN, NULL); | 1043 | SLAB_HWCACHE_ALIGN, NULL); |
1052 | if (!dccp_hashinfo.bind_bucket_cachep) | 1044 | if (!dccp_hashinfo.bind_bucket_cachep) |
1053 | goto out; | 1045 | goto out_free_percpu; |
1054 | 1046 | ||
1055 | /* | 1047 | /* |
1056 | * Size and allocate the main established and bind bucket | 1048 | * Size and allocate the main established and bind bucket |
@@ -1084,8 +1076,8 @@ static int __init dccp_init(void) | |||
1084 | } | 1076 | } |
1085 | 1077 | ||
1086 | for (i = 0; i < dccp_hashinfo.ehash_size; i++) { | 1078 | for (i = 0; i < dccp_hashinfo.ehash_size; i++) { |
1087 | INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain); | 1079 | INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i); |
1088 | INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain); | 1080 | INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i); |
1089 | } | 1081 | } |
1090 | 1082 | ||
1091 | if (inet_ehash_locks_alloc(&dccp_hashinfo)) | 1083 | if (inet_ehash_locks_alloc(&dccp_hashinfo)) |
@@ -1143,6 +1135,8 @@ out_free_dccp_ehash: | |||
1143 | out_free_bind_bucket_cachep: | 1135 | out_free_bind_bucket_cachep: |
1144 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); | 1136 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
1145 | dccp_hashinfo.bind_bucket_cachep = NULL; | 1137 | dccp_hashinfo.bind_bucket_cachep = NULL; |
1138 | out_free_percpu: | ||
1139 | percpu_counter_destroy(&dccp_orphan_count); | ||
1146 | goto out; | 1140 | goto out; |
1147 | } | 1141 | } |
1148 | 1142 | ||