diff options
author | David S. Miller <davem@davemloft.net> | 2008-09-08 20:28:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-09-08 20:28:59 -0400 |
commit | 0a68a20cc3eafa73bb54097c28b921147d7d3685 (patch) | |
tree | 8e5f315226b618cb8e050a0c7653c8ec134501e3 /net/dccp/proto.c | |
parent | 17dce5dfe38ae2fb359b61e855f5d8a3a8b7892b (diff) | |
parent | a3cbdde8e9c38b66b4f13ac5d6ff1939ded0ff20 (diff) |
Merge branch 'dccp' of git://eden-feed.erg.abdn.ac.uk/dccp_exp
Conflicts:
net/dccp/input.c
net/dccp/options.c
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r-- | net/dccp/proto.c | 281 |
1 files changed, 175 insertions, 106 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index d0bd34819761..ecf3be961e11 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -67,6 +67,9 @@ void dccp_set_state(struct sock *sk, const int state) | |||
67 | case DCCP_OPEN: | 67 | case DCCP_OPEN: |
68 | if (oldstate != DCCP_OPEN) | 68 | if (oldstate != DCCP_OPEN) |
69 | DCCP_INC_STATS(DCCP_MIB_CURRESTAB); | 69 | DCCP_INC_STATS(DCCP_MIB_CURRESTAB); |
70 | /* Client retransmits all Confirm options until entering OPEN */ | ||
71 | if (oldstate == DCCP_PARTOPEN) | ||
72 | dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg); | ||
70 | break; | 73 | break; |
71 | 74 | ||
72 | case DCCP_CLOSED: | 75 | case DCCP_CLOSED: |
@@ -175,63 +178,25 @@ EXPORT_SYMBOL_GPL(dccp_state_name); | |||
175 | int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) | 178 | int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) |
176 | { | 179 | { |
177 | struct dccp_sock *dp = dccp_sk(sk); | 180 | struct dccp_sock *dp = dccp_sk(sk); |
178 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
179 | struct inet_connection_sock *icsk = inet_csk(sk); | 181 | struct inet_connection_sock *icsk = inet_csk(sk); |
180 | 182 | ||
181 | dccp_minisock_init(&dp->dccps_minisock); | ||
182 | |||
183 | icsk->icsk_rto = DCCP_TIMEOUT_INIT; | 183 | icsk->icsk_rto = DCCP_TIMEOUT_INIT; |
184 | icsk->icsk_syn_retries = sysctl_dccp_request_retries; | 184 | icsk->icsk_syn_retries = sysctl_dccp_request_retries; |
185 | sk->sk_state = DCCP_CLOSED; | 185 | sk->sk_state = DCCP_CLOSED; |
186 | sk->sk_write_space = dccp_write_space; | 186 | sk->sk_write_space = dccp_write_space; |
187 | icsk->icsk_sync_mss = dccp_sync_mss; | 187 | icsk->icsk_sync_mss = dccp_sync_mss; |
188 | dp->dccps_mss_cache = 536; | 188 | dp->dccps_mss_cache = TCP_MIN_RCVMSS; |
189 | dp->dccps_rate_last = jiffies; | 189 | dp->dccps_rate_last = jiffies; |
190 | dp->dccps_role = DCCP_ROLE_UNDEFINED; | 190 | dp->dccps_role = DCCP_ROLE_UNDEFINED; |
191 | dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; | 191 | dp->dccps_service = DCCP_SERVICE_CODE_IS_ABSENT; |
192 | dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1; | 192 | dp->dccps_tx_qlen = sysctl_dccp_tx_qlen; |
193 | 193 | ||
194 | dccp_init_xmit_timers(sk); | 194 | dccp_init_xmit_timers(sk); |
195 | 195 | ||
196 | /* | 196 | INIT_LIST_HEAD(&dp->dccps_featneg); |
197 | * FIXME: We're hardcoding the CCID, and doing this at this point makes | 197 | /* control socket doesn't need feat nego */ |
198 | * the listening (master) sock get CCID control blocks, which is not | 198 | if (likely(ctl_sock_initialized)) |
199 | * necessary, but for now, to not mess with the test userspace apps, | 199 | 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; | 200 | return 0; |
236 | } | 201 | } |
237 | 202 | ||
@@ -240,7 +205,6 @@ EXPORT_SYMBOL_GPL(dccp_init_sock); | |||
240 | void dccp_destroy_sock(struct sock *sk) | 205 | void dccp_destroy_sock(struct sock *sk) |
241 | { | 206 | { |
242 | struct dccp_sock *dp = dccp_sk(sk); | 207 | struct dccp_sock *dp = dccp_sk(sk); |
243 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
244 | 208 | ||
245 | /* | 209 | /* |
246 | * DCCP doesn't use sk_write_queue, just sk_send_head | 210 | * DCCP doesn't use sk_write_queue, just sk_send_head |
@@ -258,7 +222,7 @@ void dccp_destroy_sock(struct sock *sk) | |||
258 | kfree(dp->dccps_service_list); | 222 | kfree(dp->dccps_service_list); |
259 | dp->dccps_service_list = NULL; | 223 | dp->dccps_service_list = NULL; |
260 | 224 | ||
261 | if (dmsk->dccpms_send_ack_vector) { | 225 | if (dp->dccps_hc_rx_ackvec != NULL) { |
262 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | 226 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); |
263 | dp->dccps_hc_rx_ackvec = NULL; | 227 | dp->dccps_hc_rx_ackvec = NULL; |
264 | } | 228 | } |
@@ -267,7 +231,7 @@ void dccp_destroy_sock(struct sock *sk) | |||
267 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | 231 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; |
268 | 232 | ||
269 | /* clean up feature negotiation state */ | 233 | /* clean up feature negotiation state */ |
270 | dccp_feat_clean(dmsk); | 234 | dccp_feat_list_purge(&dp->dccps_featneg); |
271 | } | 235 | } |
272 | 236 | ||
273 | EXPORT_SYMBOL_GPL(dccp_destroy_sock); | 237 | EXPORT_SYMBOL_GPL(dccp_destroy_sock); |
@@ -277,6 +241,9 @@ static inline int dccp_listen_start(struct sock *sk, int backlog) | |||
277 | struct dccp_sock *dp = dccp_sk(sk); | 241 | struct dccp_sock *dp = dccp_sk(sk); |
278 | 242 | ||
279 | dp->dccps_role = DCCP_ROLE_LISTEN; | 243 | dp->dccps_role = DCCP_ROLE_LISTEN; |
244 | /* do not start to listen if feature negotiation setup fails */ | ||
245 | if (dccp_feat_finalise_settings(dp)) | ||
246 | return -EPROTO; | ||
280 | return inet_csk_listen_start(sk, backlog); | 247 | return inet_csk_listen_start(sk, backlog); |
281 | } | 248 | } |
282 | 249 | ||
@@ -466,42 +433,70 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service, | |||
466 | return 0; | 433 | return 0; |
467 | } | 434 | } |
468 | 435 | ||
469 | /* byte 1 is feature. the rest is the preference list */ | 436 | 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 | { | 437 | { |
473 | struct dccp_so_feat opt; | 438 | u8 *list, len; |
474 | u8 *val; | 439 | int i, rc; |
475 | int rc; | ||
476 | 440 | ||
477 | if (copy_from_user(&opt, optval, sizeof(opt))) | 441 | if (cscov < 0 || cscov > 15) |
478 | return -EFAULT; | 442 | return -EINVAL; |
479 | /* | 443 | /* |
480 | * rfc4340: 6.1. Change Options | 444 | * Populate a list of permissible values, in the range cscov...15. This |
445 | * is necessary since feature negotiation of single values only works if | ||
446 | * both sides incidentally choose the same value. Since the list starts | ||
447 | * lowest-value first, negotiation will pick the smallest shared value. | ||
481 | */ | 448 | */ |
482 | if (opt.dccpsf_len < 1) | 449 | if (cscov == 0) |
450 | return 0; | ||
451 | len = 16 - cscov; | ||
452 | |||
453 | list = kmalloc(len, GFP_KERNEL); | ||
454 | if (list == NULL) | ||
455 | return -ENOBUFS; | ||
456 | |||
457 | for (i = 0; i < len; i++) | ||
458 | list[i] = cscov++; | ||
459 | |||
460 | rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len); | ||
461 | |||
462 | if (rc == 0) { | ||
463 | if (rx) | ||
464 | dccp_sk(sk)->dccps_pcrlen = cscov; | ||
465 | else | ||
466 | dccp_sk(sk)->dccps_pcslen = cscov; | ||
467 | } | ||
468 | kfree(list); | ||
469 | return rc; | ||
470 | } | ||
471 | |||
472 | static int dccp_setsockopt_ccid(struct sock *sk, int type, | ||
473 | char __user *optval, int optlen) | ||
474 | { | ||
475 | u8 *val; | ||
476 | int rc = 0; | ||
477 | |||
478 | if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS) | ||
483 | return -EINVAL; | 479 | return -EINVAL; |
484 | 480 | ||
485 | val = kmalloc(opt.dccpsf_len, GFP_KERNEL); | 481 | val = kmalloc(optlen, GFP_KERNEL); |
486 | if (!val) | 482 | if (val == NULL) |
487 | return -ENOMEM; | 483 | return -ENOMEM; |
488 | 484 | ||
489 | if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { | 485 | if (copy_from_user(val, optval, optlen)) { |
490 | rc = -EFAULT; | 486 | kfree(val); |
491 | goto out_free_val; | 487 | return -EFAULT; |
492 | } | 488 | } |
493 | 489 | ||
494 | rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, | 490 | lock_sock(sk); |
495 | val, opt.dccpsf_len, GFP_KERNEL); | 491 | if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID) |
496 | if (rc) | 492 | rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen); |
497 | goto out_free_val; | ||
498 | 493 | ||
499 | out: | 494 | if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID)) |
500 | return rc; | 495 | rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen); |
496 | release_sock(sk); | ||
501 | 497 | ||
502 | out_free_val: | ||
503 | kfree(val); | 498 | kfree(val); |
504 | goto out; | 499 | return rc; |
505 | } | 500 | } |
506 | 501 | ||
507 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | 502 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, |
@@ -510,7 +505,21 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
510 | struct dccp_sock *dp = dccp_sk(sk); | 505 | struct dccp_sock *dp = dccp_sk(sk); |
511 | int val, err = 0; | 506 | int val, err = 0; |
512 | 507 | ||
513 | if (optlen < sizeof(int)) | 508 | switch (optname) { |
509 | case DCCP_SOCKOPT_PACKET_SIZE: | ||
510 | DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n"); | ||
511 | return 0; | ||
512 | case DCCP_SOCKOPT_CHANGE_L: | ||
513 | case DCCP_SOCKOPT_CHANGE_R: | ||
514 | DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n"); | ||
515 | return 0; | ||
516 | case DCCP_SOCKOPT_CCID: | ||
517 | case DCCP_SOCKOPT_RX_CCID: | ||
518 | case DCCP_SOCKOPT_TX_CCID: | ||
519 | return dccp_setsockopt_ccid(sk, optname, optval, optlen); | ||
520 | } | ||
521 | |||
522 | if (optlen < (int)sizeof(int)) | ||
514 | return -EINVAL; | 523 | return -EINVAL; |
515 | 524 | ||
516 | if (get_user(val, (int __user *)optval)) | 525 | if (get_user(val, (int __user *)optval)) |
@@ -521,53 +530,38 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | |||
521 | 530 | ||
522 | lock_sock(sk); | 531 | lock_sock(sk); |
523 | switch (optname) { | 532 | 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: | 533 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: |
545 | if (dp->dccps_role != DCCP_ROLE_SERVER) | 534 | if (dp->dccps_role != DCCP_ROLE_SERVER) |
546 | err = -EOPNOTSUPP; | 535 | err = -EOPNOTSUPP; |
547 | else | 536 | else |
548 | dp->dccps_server_timewait = (val != 0); | 537 | dp->dccps_server_timewait = (val != 0); |
549 | break; | 538 | break; |
550 | case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */ | 539 | case DCCP_SOCKOPT_SEND_CSCOV: |
551 | if (val < 0 || val > 15) | 540 | err = dccp_setsockopt_cscov(sk, val, false); |
541 | break; | ||
542 | case DCCP_SOCKOPT_RECV_CSCOV: | ||
543 | err = dccp_setsockopt_cscov(sk, val, true); | ||
544 | break; | ||
545 | case DCCP_SOCKOPT_QPOLICY_ID: | ||
546 | if (sk->sk_state != DCCP_CLOSED) | ||
547 | err = -EISCONN; | ||
548 | else if (val < 0 || val >= DCCPQ_POLICY_MAX) | ||
552 | err = -EINVAL; | 549 | err = -EINVAL; |
553 | else | 550 | else |
554 | dp->dccps_pcslen = val; | 551 | dp->dccps_qpolicy = val; |
555 | break; | 552 | break; |
556 | case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */ | 553 | case DCCP_SOCKOPT_QPOLICY_TXQLEN: |
557 | if (val < 0 || val > 15) | 554 | if (val < 0) |
558 | err = -EINVAL; | 555 | err = -EINVAL; |
559 | else { | 556 | else |
560 | dp->dccps_pcrlen = val; | 557 | dp->dccps_tx_qlen = val; |
561 | /* FIXME: add feature negotiation, | ||
562 | * ChangeL(MinimumChecksumCoverage, val) */ | ||
563 | } | ||
564 | break; | 558 | break; |
565 | default: | 559 | default: |
566 | err = -ENOPROTOOPT; | 560 | err = -ENOPROTOOPT; |
567 | break; | 561 | break; |
568 | } | 562 | } |
569 | |||
570 | release_sock(sk); | 563 | release_sock(sk); |
564 | |||
571 | return err; | 565 | return err; |
572 | } | 566 | } |
573 | 567 | ||
@@ -648,6 +642,18 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, | |||
648 | case DCCP_SOCKOPT_GET_CUR_MPS: | 642 | case DCCP_SOCKOPT_GET_CUR_MPS: |
649 | val = dp->dccps_mss_cache; | 643 | val = dp->dccps_mss_cache; |
650 | break; | 644 | break; |
645 | case DCCP_SOCKOPT_AVAILABLE_CCIDS: | ||
646 | return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); | ||
647 | case DCCP_SOCKOPT_TX_CCID: | ||
648 | val = ccid_get_current_tx_ccid(dp); | ||
649 | if (val < 0) | ||
650 | return -ENOPROTOOPT; | ||
651 | break; | ||
652 | case DCCP_SOCKOPT_RX_CCID: | ||
653 | val = ccid_get_current_rx_ccid(dp); | ||
654 | if (val < 0) | ||
655 | return -ENOPROTOOPT; | ||
656 | break; | ||
651 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: | 657 | case DCCP_SOCKOPT_SERVER_TIMEWAIT: |
652 | val = dp->dccps_server_timewait; | 658 | val = dp->dccps_server_timewait; |
653 | break; | 659 | break; |
@@ -657,6 +663,12 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, | |||
657 | case DCCP_SOCKOPT_RECV_CSCOV: | 663 | case DCCP_SOCKOPT_RECV_CSCOV: |
658 | val = dp->dccps_pcrlen; | 664 | val = dp->dccps_pcrlen; |
659 | break; | 665 | break; |
666 | case DCCP_SOCKOPT_QPOLICY_ID: | ||
667 | val = dp->dccps_qpolicy; | ||
668 | break; | ||
669 | case DCCP_SOCKOPT_QPOLICY_TXQLEN: | ||
670 | val = dp->dccps_tx_qlen; | ||
671 | break; | ||
660 | case 128 ... 191: | 672 | case 128 ... 191: |
661 | return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, | 673 | return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, |
662 | len, (u32 __user *)optval, optlen); | 674 | len, (u32 __user *)optval, optlen); |
@@ -699,6 +711,47 @@ int compat_dccp_getsockopt(struct sock *sk, int level, int optname, | |||
699 | EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); | 711 | EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); |
700 | #endif | 712 | #endif |
701 | 713 | ||
714 | static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) | ||
715 | { | ||
716 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); | ||
717 | |||
718 | /* | ||
719 | * Assign an (opaque) qpolicy priority value to skb->priority. | ||
720 | * | ||
721 | * We are overloading this skb field for use with the qpolicy subystem. | ||
722 | * The skb->priority is normally used for the SO_PRIORITY option, which | ||
723 | * is initialised from sk_priority. Since the assignment of sk_priority | ||
724 | * to skb->priority happens later (on layer 3), we overload this field | ||
725 | * for use with queueing priorities as long as the skb is on layer 4. | ||
726 | * The default priority value (if nothing is set) is 0. | ||
727 | */ | ||
728 | skb->priority = 0; | ||
729 | |||
730 | for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { | ||
731 | |||
732 | if (!CMSG_OK(msg, cmsg)) | ||
733 | return -EINVAL; | ||
734 | |||
735 | if (cmsg->cmsg_level != SOL_DCCP) | ||
736 | continue; | ||
737 | |||
738 | if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX && | ||
739 | !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type)) | ||
740 | return -EINVAL; | ||
741 | |||
742 | switch (cmsg->cmsg_type) { | ||
743 | case DCCP_SCM_PRIORITY: | ||
744 | if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32))) | ||
745 | return -EINVAL; | ||
746 | skb->priority = *(__u32 *)CMSG_DATA(cmsg); | ||
747 | break; | ||
748 | default: | ||
749 | return -EINVAL; | ||
750 | } | ||
751 | } | ||
752 | return 0; | ||
753 | } | ||
754 | |||
702 | int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 755 | int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, |
703 | size_t len) | 756 | size_t len) |
704 | { | 757 | { |
@@ -714,8 +767,7 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
714 | 767 | ||
715 | lock_sock(sk); | 768 | lock_sock(sk); |
716 | 769 | ||
717 | if (sysctl_dccp_tx_qlen && | 770 | if (dccp_qpolicy_full(sk)) { |
718 | (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) { | ||
719 | rc = -EAGAIN; | 771 | rc = -EAGAIN; |
720 | goto out_release; | 772 | goto out_release; |
721 | } | 773 | } |
@@ -743,8 +795,12 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
743 | if (rc != 0) | 795 | if (rc != 0) |
744 | goto out_discard; | 796 | goto out_discard; |
745 | 797 | ||
746 | skb_queue_tail(&sk->sk_write_queue, skb); | 798 | rc = dccp_msghdr_parse(msg, skb); |
747 | dccp_write_xmit(sk,0); | 799 | if (rc != 0) |
800 | goto out_discard; | ||
801 | |||
802 | dccp_qpolicy_push(sk, skb); | ||
803 | dccp_write_xmit(sk); | ||
748 | out_release: | 804 | out_release: |
749 | release_sock(sk); | 805 | release_sock(sk); |
750 | return rc ? : len; | 806 | return rc ? : len; |
@@ -967,9 +1023,22 @@ void dccp_close(struct sock *sk, long timeout) | |||
967 | /* Check zero linger _after_ checking for unread data. */ | 1023 | /* Check zero linger _after_ checking for unread data. */ |
968 | sk->sk_prot->disconnect(sk, 0); | 1024 | sk->sk_prot->disconnect(sk, 0); |
969 | } else if (sk->sk_state != DCCP_CLOSED) { | 1025 | } else if (sk->sk_state != DCCP_CLOSED) { |
1026 | /* | ||
1027 | * Normal connection termination. May need to wait if there are | ||
1028 | * still packets in the TX queue that are delayed by the CCID. | ||
1029 | */ | ||
1030 | dccp_flush_write_queue(sk, &timeout); | ||
970 | dccp_terminate_connection(sk); | 1031 | dccp_terminate_connection(sk); |
971 | } | 1032 | } |
972 | 1033 | ||
1034 | /* | ||
1035 | * Flush write queue. This may be necessary in several cases: | ||
1036 | * - we have been closed by the peer but still have application data; | ||
1037 | * - abortive termination (unread data or zero linger time), | ||
1038 | * - normal termination but queue could not be flushed within time limit | ||
1039 | */ | ||
1040 | __skb_queue_purge(&sk->sk_write_queue); | ||
1041 | |||
973 | sk_stream_wait_close(sk, timeout); | 1042 | sk_stream_wait_close(sk, timeout); |
974 | 1043 | ||
975 | adjudge_to_death: | 1044 | adjudge_to_death: |