aboutsummaryrefslogtreecommitdiffstats
path: root/net/dccp/proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r--net/dccp/proto.c53
1 files changed, 53 insertions, 0 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 81ad249537106b..1a8cf8ecfe63e3 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -37,6 +37,7 @@
#include "ccid.h"
#include "dccp.h"
+#include "feat.h"
DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
@@ -255,6 +256,39 @@ static int dccp_setsockopt_service(struct sock *sk, const u32 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;
+
+ 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(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;
+}
+
int dccp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen)
{
@@ -284,6 +318,25 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
case DCCP_SOCKOPT_PACKET_SIZE:
dp->dccps_packet_size = val;
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 *)
+ 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 *)
+ optval);
+ break;
+
default:
err = -ENOPROTOOPT;
break;