diff options
Diffstat (limited to 'net/dccp/proto.c')
| -rw-r--r-- | net/dccp/proto.c | 94 |
1 files changed, 90 insertions, 4 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 18a0e69c9dc7..a1cfd0e9e3bc 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
| @@ -94,7 +94,15 @@ EXPORT_SYMBOL_GPL(dccp_state_name); | |||
| 94 | 94 | ||
| 95 | static inline int dccp_listen_start(struct sock *sk) | 95 | static inline int dccp_listen_start(struct sock *sk) |
| 96 | { | 96 | { |
| 97 | dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN; | 97 | struct dccp_sock *dp = dccp_sk(sk); |
| 98 | |||
| 99 | dp->dccps_role = DCCP_ROLE_LISTEN; | ||
| 100 | /* | ||
| 101 | * Apps need to use setsockopt(DCCP_SOCKOPT_SERVICE) | ||
| 102 | * before calling listen() | ||
| 103 | */ | ||
| 104 | if (dccp_service_not_initialized(sk)) | ||
| 105 | return -EPROTO; | ||
| 98 | return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); | 106 | return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); |
| 99 | } | 107 | } |
| 100 | 108 | ||
| @@ -202,6 +210,42 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) | |||
| 202 | return -ENOIOCTLCMD; | 210 | return -ENOIOCTLCMD; |
| 203 | } | 211 | } |
| 204 | 212 | ||
| 213 | static int dccp_setsockopt_service(struct sock *sk, const u32 service, | ||
| 214 | char __user *optval, int optlen) | ||
| 215 | { | ||
| 216 | struct dccp_sock *dp = dccp_sk(sk); | ||
| 217 | struct dccp_service_list *sl = NULL; | ||
| 218 | |||
| 219 | if (service == DCCP_SERVICE_INVALID_VALUE || | ||
| 220 | optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32)) | ||
| 221 | return -EINVAL; | ||
| 222 | |||
| 223 | if (optlen > sizeof(service)) { | ||
| 224 | sl = kmalloc(optlen, GFP_KERNEL); | ||
| 225 | if (sl == NULL) | ||
| 226 | return -ENOMEM; | ||
| 227 | |||
| 228 | sl->dccpsl_nr = optlen / sizeof(u32) - 1; | ||
| 229 | if (copy_from_user(sl->dccpsl_list, | ||
| 230 | optval + sizeof(service), | ||
| 231 | optlen - sizeof(service)) || | ||
| 232 | dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) { | ||
| 233 | kfree(sl); | ||
| 234 | return -EFAULT; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | lock_sock(sk); | ||
| 239 | dp->dccps_service = service; | ||
| 240 | |||
| 241 | if (dp->dccps_service_list != NULL) | ||
| 242 | kfree(dp->dccps_service_list); | ||
| 243 | |||
| 244 | dp->dccps_service_list = sl; | ||
| 245 | release_sock(sk); | ||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 205 | int dccp_setsockopt(struct sock *sk, int level, int optname, | 249 | int dccp_setsockopt(struct sock *sk, int level, int optname, |
| 206 | char __user *optval, int optlen) | 250 | char __user *optval, int optlen) |
| 207 | { | 251 | { |
| @@ -218,8 +262,10 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, | |||
| 218 | if (get_user(val, (int __user *)optval)) | 262 | if (get_user(val, (int __user *)optval)) |
| 219 | return -EFAULT; | 263 | return -EFAULT; |
| 220 | 264 | ||
| 221 | lock_sock(sk); | 265 | if (optname == DCCP_SOCKOPT_SERVICE) |
| 266 | return dccp_setsockopt_service(sk, val, optval, optlen); | ||
| 222 | 267 | ||
| 268 | lock_sock(sk); | ||
| 223 | dp = dccp_sk(sk); | 269 | dp = dccp_sk(sk); |
| 224 | err = 0; | 270 | err = 0; |
| 225 | 271 | ||
| @@ -236,6 +282,37 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, | |||
| 236 | return err; | 282 | return err; |
| 237 | } | 283 | } |
| 238 | 284 | ||
| 285 | static int dccp_getsockopt_service(struct sock *sk, int len, | ||
| 286 | u32 __user *optval, | ||
| 287 | int __user *optlen) | ||
| 288 | { | ||
| 289 | const struct dccp_sock *dp = dccp_sk(sk); | ||
| 290 | const struct dccp_service_list *sl; | ||
| 291 | int err = -ENOENT, slen = 0, total_len = sizeof(u32); | ||
| 292 | |||
| 293 | lock_sock(sk); | ||
| 294 | if (dccp_service_not_initialized(sk)) | ||
| 295 | goto out; | ||
| 296 | |||
| 297 | if ((sl = dp->dccps_service_list) != NULL) { | ||
| 298 | slen = sl->dccpsl_nr * sizeof(u32); | ||
| 299 | total_len += slen; | ||
| 300 | } | ||
| 301 | |||
| 302 | err = -EINVAL; | ||
| 303 | if (total_len > len) | ||
| 304 | goto out; | ||
| 305 | |||
| 306 | err = 0; | ||
| 307 | if (put_user(total_len, optlen) || | ||
| 308 | put_user(dp->dccps_service, optval) || | ||
| 309 | (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen))) | ||
| 310 | err = -EFAULT; | ||
| 311 | out: | ||
| 312 | release_sock(sk); | ||
| 313 | return err; | ||
| 314 | } | ||
| 315 | |||
| 239 | int dccp_getsockopt(struct sock *sk, int level, int optname, | 316 | int dccp_getsockopt(struct sock *sk, int level, int optname, |
| 240 | char __user *optval, int __user *optlen) | 317 | char __user *optval, int __user *optlen) |
| 241 | { | 318 | { |
| @@ -248,8 +325,7 @@ int dccp_getsockopt(struct sock *sk, int level, int optname, | |||
| 248 | if (get_user(len, optlen)) | 325 | if (get_user(len, optlen)) |
| 249 | return -EFAULT; | 326 | return -EFAULT; |
| 250 | 327 | ||
| 251 | len = min_t(unsigned int, len, sizeof(int)); | 328 | if (len < sizeof(int)) |
| 252 | if (len < 0) | ||
| 253 | return -EINVAL; | 329 | return -EINVAL; |
| 254 | 330 | ||
| 255 | dp = dccp_sk(sk); | 331 | dp = dccp_sk(sk); |
| @@ -257,7 +333,17 @@ int dccp_getsockopt(struct sock *sk, int level, int optname, | |||
| 257 | switch (optname) { | 333 | switch (optname) { |
| 258 | case DCCP_SOCKOPT_PACKET_SIZE: | 334 | case DCCP_SOCKOPT_PACKET_SIZE: |
| 259 | val = dp->dccps_packet_size; | 335 | val = dp->dccps_packet_size; |
| 336 | len = sizeof(dp->dccps_packet_size); | ||
| 260 | break; | 337 | break; |
| 338 | case DCCP_SOCKOPT_SERVICE: | ||
| 339 | return dccp_getsockopt_service(sk, len, | ||
| 340 | (u32 __user *)optval, optlen); | ||
| 341 | case 128 ... 191: | ||
| 342 | return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, | ||
| 343 | len, (u32 __user *)optval, optlen); | ||
| 344 | case 192 ... 255: | ||
| 345 | return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname, | ||
| 346 | len, (u32 __user *)optval, optlen); | ||
| 261 | default: | 347 | default: |
| 262 | return -ENOPROTOOPT; | 348 | return -ENOPROTOOPT; |
| 263 | } | 349 | } |
