Merge branch 'master' into for-upstream
[pandora-kernel.git] / net / dccp / proto.c
index b56efdd..d0bd348 100644 (file)
@@ -67,9 +67,6 @@ void dccp_set_state(struct sock *sk, const int state)
        case DCCP_OPEN:
                if (oldstate != DCCP_OPEN)
                        DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
-               /* Client retransmits all Confirm options until entering OPEN */
-               if (oldstate == DCCP_PARTOPEN)
-                       dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg);
                break;
 
        case DCCP_CLOSED:
@@ -178,8 +175,11 @@ EXPORT_SYMBOL_GPL(dccp_state_name);
 int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
 {
        struct dccp_sock *dp = dccp_sk(sk);
+       struct dccp_minisock *dmsk = dccp_msk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
 
+       dccp_minisock_init(&dp->dccps_minisock);
+
        icsk->icsk_rto          = DCCP_TIMEOUT_INIT;
        icsk->icsk_syn_retries  = sysctl_dccp_request_retries;
        sk->sk_state            = DCCP_CLOSED;
@@ -189,14 +189,49 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
        dp->dccps_rate_last     = jiffies;
        dp->dccps_role          = DCCP_ROLE_UNDEFINED;
        dp->dccps_service       = DCCP_SERVICE_CODE_IS_ABSENT;
-       dp->dccps_tx_qlen       = sysctl_dccp_tx_qlen;
+       dp->dccps_l_ack_ratio   = dp->dccps_r_ack_ratio = 1;
 
        dccp_init_xmit_timers(sk);
 
-       INIT_LIST_HEAD(&dp->dccps_featneg);
-       /* control socket doesn't need feat nego */
-       if (likely(ctl_sock_initialized))
-               return dccp_feat_init(sk);
+       /*
+        * FIXME: We're hardcoding the CCID, and doing this at this point makes
+        * the listening (master) sock get CCID control blocks, which is not
+        * necessary, but for now, to not mess with the test userspace apps,
+        * lets leave it here, later the real solution is to do this in a
+        * setsockopt(CCIDs-I-want/accept). -acme
+        */
+       if (likely(ctl_sock_initialized)) {
+               int rc = dccp_feat_init(dmsk);
+
+               if (rc)
+                       return rc;
+
+               if (dmsk->dccpms_send_ack_vector) {
+                       dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
+                       if (dp->dccps_hc_rx_ackvec == NULL)
+                               return -ENOMEM;
+               }
+               dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
+                                                     sk, GFP_KERNEL);
+               dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
+                                                     sk, GFP_KERNEL);
+               if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
+                            dp->dccps_hc_tx_ccid == NULL)) {
+                       ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+                       ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+                       if (dmsk->dccpms_send_ack_vector) {
+                               dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
+                               dp->dccps_hc_rx_ackvec = NULL;
+                       }
+                       dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+                       return -ENOMEM;
+               }
+       } else {
+               /* control socket doesn't need feat nego */
+               INIT_LIST_HEAD(&dmsk->dccpms_pending);
+               INIT_LIST_HEAD(&dmsk->dccpms_conf);
+       }
+
        return 0;
 }
 
@@ -205,6 +240,7 @@ EXPORT_SYMBOL_GPL(dccp_init_sock);
 void dccp_destroy_sock(struct sock *sk)
 {
        struct dccp_sock *dp = dccp_sk(sk);
+       struct dccp_minisock *dmsk = dccp_msk(sk);
 
        /*
         * DCCP doesn't use sk_write_queue, just sk_send_head
@@ -222,7 +258,7 @@ void dccp_destroy_sock(struct sock *sk)
        kfree(dp->dccps_service_list);
        dp->dccps_service_list = NULL;
 
-       if (dp->dccps_hc_rx_ackvec != NULL) {
+       if (dmsk->dccpms_send_ack_vector) {
                dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
                dp->dccps_hc_rx_ackvec = NULL;
        }
@@ -231,7 +267,7 @@ void dccp_destroy_sock(struct sock *sk)
        dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
 
        /* clean up feature negotiation state */
-       dccp_feat_list_purge(&dp->dccps_featneg);
+       dccp_feat_clean(dmsk);
 }
 
 EXPORT_SYMBOL_GPL(dccp_destroy_sock);
@@ -241,9 +277,6 @@ static inline int dccp_listen_start(struct sock *sk, int backlog)
        struct dccp_sock *dp = dccp_sk(sk);
 
        dp->dccps_role = DCCP_ROLE_LISTEN;
-       /* do not start to listen if feature negotiation setup fails */
-       if (dccp_feat_finalise_settings(dp))
-               return -EPROTO;
        return inet_csk_listen_start(sk, backlog);
 }
 
@@ -433,70 +466,42 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
        return 0;
 }
 
-static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx)
+/* byte 1 is feature.  the rest is the preference list */
+static int dccp_setsockopt_change(struct sock *sk, int type,
+                                 struct dccp_so_feat __user *optval)
 {
-       u8 *list, len;
-       int i, rc;
+       struct dccp_so_feat opt;
+       u8 *val;
+       int rc;
 
-       if (cscov < 0 || cscov > 15)
-               return -EINVAL;
+       if (copy_from_user(&opt, optval, sizeof(opt)))
+               return -EFAULT;
        /*
-        * Populate a list of permissible values, in the range cscov...15. This
-        * is necessary since feature negotiation of single values only works if
-        * both sides incidentally choose the same value. Since the list starts
-        * lowest-value first, negotiation will pick the smallest shared value.
+        * rfc4340: 6.1. Change Options
         */
-       if (cscov == 0)
-               return 0;
-       len = 16 - cscov;
-
-       list = kmalloc(len, GFP_KERNEL);
-       if (list == NULL)
-               return -ENOBUFS;
-
-       for (i = 0; i < len; i++)
-               list[i] = cscov++;
-
-       rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len);
-
-       if (rc == 0) {
-               if (rx)
-                       dccp_sk(sk)->dccps_pcrlen = cscov;
-               else
-                       dccp_sk(sk)->dccps_pcslen = cscov;
-       }
-       kfree(list);
-       return rc;
-}
-
-static int dccp_setsockopt_ccid(struct sock *sk, int type,
-                               char __user *optval, int optlen)
-{
-       u8 *val;
-       int rc = 0;
-
-       if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS)
+       if (opt.dccpsf_len < 1)
                return -EINVAL;
 
-       val = kmalloc(optlen, GFP_KERNEL);
-       if (val == NULL)
+       val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
+       if (!val)
                return -ENOMEM;
 
-       if (copy_from_user(val, optval, optlen)) {
-               kfree(val);
-               return -EFAULT;
+       if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
+               rc = -EFAULT;
+               goto out_free_val;
        }
 
-       lock_sock(sk);
-       if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID)
-               rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen);
+       rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
+                             val, opt.dccpsf_len, GFP_KERNEL);
+       if (rc)
+               goto out_free_val;
 
-       if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID))
-               rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen);
-       release_sock(sk);
+out:
+       return rc;
 
+out_free_val:
        kfree(val);
-       return rc;
+       goto out;
 }
 
 static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
@@ -505,21 +510,7 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
        struct dccp_sock *dp = dccp_sk(sk);
        int val, err = 0;
 
-       switch (optname) {
-       case DCCP_SOCKOPT_PACKET_SIZE:
-               DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
-               return 0;
-       case DCCP_SOCKOPT_CHANGE_L:
-       case DCCP_SOCKOPT_CHANGE_R:
-               DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
-               return 0;
-       case DCCP_SOCKOPT_CCID:
-       case DCCP_SOCKOPT_RX_CCID:
-       case DCCP_SOCKOPT_TX_CCID:
-               return dccp_setsockopt_ccid(sk, optname, optval, optlen);
-       }
-
-       if (optlen < (int)sizeof(int))
+       if (optlen < sizeof(int))
                return -EINVAL;
 
        if (get_user(val, (int __user *)optval))
@@ -530,38 +521,53 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
 
        lock_sock(sk);
        switch (optname) {
+       case DCCP_SOCKOPT_PACKET_SIZE:
+               DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
+               err = 0;
+               break;
+       case DCCP_SOCKOPT_CHANGE_L:
+               if (optlen != sizeof(struct dccp_so_feat))
+                       err = -EINVAL;
+               else
+                       err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
+                                                    (struct dccp_so_feat __user *)
+                                                    optval);
+               break;
+       case DCCP_SOCKOPT_CHANGE_R:
+               if (optlen != sizeof(struct dccp_so_feat))
+                       err = -EINVAL;
+               else
+                       err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
+                                                    (struct dccp_so_feat __user *)
+                                                    optval);
+               break;
        case DCCP_SOCKOPT_SERVER_TIMEWAIT:
                if (dp->dccps_role != DCCP_ROLE_SERVER)
                        err = -EOPNOTSUPP;
                else
                        dp->dccps_server_timewait = (val != 0);
                break;
-       case DCCP_SOCKOPT_SEND_CSCOV:
-               err = dccp_setsockopt_cscov(sk, val, false);
-               break;
-       case DCCP_SOCKOPT_RECV_CSCOV:
-               err = dccp_setsockopt_cscov(sk, val, true);
-               break;
-       case DCCP_SOCKOPT_QPOLICY_ID:
-               if (sk->sk_state != DCCP_CLOSED)
-                       err = -EISCONN;
-               else if (val < 0 || val >= DCCPQ_POLICY_MAX)
+       case DCCP_SOCKOPT_SEND_CSCOV:   /* sender side, RFC 4340, sec. 9.2 */
+               if (val < 0 || val > 15)
                        err = -EINVAL;
                else
-                       dp->dccps_qpolicy = val;
+                       dp->dccps_pcslen = val;
                break;
-       case DCCP_SOCKOPT_QPOLICY_TXQLEN:
-               if (val < 0)
+       case DCCP_SOCKOPT_RECV_CSCOV:   /* receiver side, RFC 4340 sec. 9.2.1 */
+               if (val < 0 || val > 15)
                        err = -EINVAL;
-               else
-                       dp->dccps_tx_qlen = val;
+               else {
+                       dp->dccps_pcrlen = val;
+                       /* FIXME: add feature negotiation,
+                        * ChangeL(MinimumChecksumCoverage, val) */
+               }
                break;
        default:
                err = -ENOPROTOOPT;
                break;
        }
-       release_sock(sk);
 
+       release_sock(sk);
        return err;
 }
 
@@ -642,18 +648,6 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
        case DCCP_SOCKOPT_GET_CUR_MPS:
                val = dp->dccps_mss_cache;
                break;
-       case DCCP_SOCKOPT_AVAILABLE_CCIDS:
-               return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
-       case DCCP_SOCKOPT_TX_CCID:
-               val = ccid_get_current_tx_ccid(dp);
-               if (val < 0)
-                       return -ENOPROTOOPT;
-               break;
-       case DCCP_SOCKOPT_RX_CCID:
-               val = ccid_get_current_rx_ccid(dp);
-               if (val < 0)
-                       return -ENOPROTOOPT;
-               break;
        case DCCP_SOCKOPT_SERVER_TIMEWAIT:
                val = dp->dccps_server_timewait;
                break;
@@ -663,12 +657,6 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
        case DCCP_SOCKOPT_RECV_CSCOV:
                val = dp->dccps_pcrlen;
                break;
-       case DCCP_SOCKOPT_QPOLICY_ID:
-               val = dp->dccps_qpolicy;
-               break;
-       case DCCP_SOCKOPT_QPOLICY_TXQLEN:
-               val = dp->dccps_tx_qlen;
-               break;
        case 128 ... 191:
                return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
                                             len, (u32 __user *)optval, optlen);
@@ -711,43 +699,6 @@ int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
 #endif
 
-static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
-{
-       struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
-
-       /*
-        * Assign an (opaque) qpolicy priority value to skb->priority.
-        *
-        * We are overloading this skb field for use with the qpolicy subystem.
-        * The skb->priority is normally used for the SO_PRIORITY option, which
-        * is initialised from sk_priority. Since the assignment of sk_priority
-        * to skb->priority happens later (on layer 3), we overload this field
-        * for use with queueing priorities as long as the skb is on layer 4.
-        * The default priority value (if nothing is set) is 0.
-        */
-       skb->priority = 0;
-
-       for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-
-               if (!CMSG_OK(msg, cmsg))
-                       return -EINVAL;
-
-               if (cmsg->cmsg_level != SOL_DCCP)
-                       continue;
-
-               switch (cmsg->cmsg_type) {
-               case DCCP_SCM_PRIORITY:
-                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32)))
-                               return -EINVAL;
-                       skb->priority = *(__u32 *)CMSG_DATA(cmsg);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-       return 0;
-}
-
 int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                 size_t len)
 {
@@ -763,7 +714,8 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
        lock_sock(sk);
 
-       if (dccp_qpolicy_full(sk)) {
+       if (sysctl_dccp_tx_qlen &&
+           (sk->sk_write_queue.qlen >= sysctl_dccp_tx_qlen)) {
                rc = -EAGAIN;
                goto out_release;
        }
@@ -791,12 +743,8 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (rc != 0)
                goto out_discard;
 
-       rc = dccp_msghdr_parse(msg, skb);
-       if (rc != 0)
-               goto out_discard;
-
-       dccp_qpolicy_push(sk, skb);
-       dccp_write_xmit(sk);
+       skb_queue_tail(&sk->sk_write_queue, skb);
+       dccp_write_xmit(sk,0);
 out_release:
        release_sock(sk);
        return rc ? : len;
@@ -1019,22 +967,9 @@ void dccp_close(struct sock *sk, long timeout)
                /* Check zero linger _after_ checking for unread data. */
                sk->sk_prot->disconnect(sk, 0);
        } else if (sk->sk_state != DCCP_CLOSED) {
-               /*
-                * Normal connection termination. May need to wait if there are
-                * still packets in the TX queue that are delayed by the CCID.
-                */
-               dccp_flush_write_queue(sk, &timeout);
                dccp_terminate_connection(sk);
        }
 
-       /*
-        * Flush write queue. This may be necessary in several cases:
-        * - we have been closed by the peer but still have application data;
-        * - abortive termination (unread data or zero linger time),
-        * - normal termination but queue could not be flushed within time limit
-        */
-       __skb_queue_purge(&sk->sk_write_queue);
-
        sk_stream_wait_close(sk, timeout);
 
 adjudge_to_death: