Merge branch 'master' into for-upstream
[pandora-kernel.git] / net / dccp / proto.c
index 108d56b..d0bd348 100644 (file)
@@ -193,7 +193,6 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
 
        dccp_init_xmit_timers(sk);
 
-       INIT_LIST_HEAD(&dp->dccps_featneg);
        /*
         * FIXME: We're hardcoding the CCID, and doing this at this point makes
         * the listening (master) sock get CCID control blocks, which is not
@@ -202,7 +201,7 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
         * setsockopt(CCIDs-I-want/accept). -acme
         */
        if (likely(ctl_sock_initialized)) {
-               int rc = dccp_feat_init(sk);
+               int rc = dccp_feat_init(dmsk);
 
                if (rc)
                        return rc;
@@ -268,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);
@@ -278,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);
 }
 
@@ -470,6 +466,44 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
        return 0;
 }
 
+/* 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)
+{
+       struct dccp_so_feat opt;
+       u8 *val;
+       int rc;
+
+       if (copy_from_user(&opt, optval, sizeof(opt)))
+               return -EFAULT;
+       /*
+        * rfc4340: 6.1. Change Options
+        */
+       if (opt.dccpsf_len < 1)
+               return -EINVAL;
+
+       val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
+       if (!val)
+               return -ENOMEM;
+
+       if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
+               rc = -EFAULT;
+               goto out_free_val;
+       }
+
+       rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
+                             val, opt.dccpsf_len, GFP_KERNEL);
+       if (rc)
+               goto out_free_val;
+
+out:
+       return rc;
+
+out_free_val:
+       kfree(val);
+       goto out;
+}
+
 static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
                char __user *optval, int optlen)
 {
@@ -492,9 +526,20 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
                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:
-               DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
-               err = 0;
+               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)
@@ -603,8 +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_SERVER_TIMEWAIT:
                val = dp->dccps_server_timewait;
                break;