diff options
-rw-r--r-- | include/linux/dccp.h | 40 | ||||
-rw-r--r-- | net/dccp/dccp.h | 9 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 30 | ||||
-rw-r--r-- | net/dccp/minisocks.c | 6 | ||||
-rw-r--r-- | net/dccp/output.c | 14 | ||||
-rw-r--r-- | net/dccp/proto.c | 85 |
6 files changed, 154 insertions, 30 deletions
diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 8bf4bacb5051..0e72708677e4 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h | |||
@@ -4,16 +4,6 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <asm/byteorder.h> | 5 | #include <asm/byteorder.h> |
6 | 6 | ||
7 | /* Structure describing an Internet (DCCP) socket address. */ | ||
8 | struct sockaddr_dccp { | ||
9 | __u16 sdccp_family; /* Address family */ | ||
10 | __u16 sdccp_port; /* Port number */ | ||
11 | __u32 sdccp_addr; /* Internet address */ | ||
12 | __u32 sdccp_service; /* Service */ | ||
13 | /* Pad to size of `struct sockaddr': 16 bytes . */ | ||
14 | __u32 sdccp_pad; | ||
15 | }; | ||
16 | |||
17 | /** | 7 | /** |
18 | * struct dccp_hdr - generic part of DCCP packet header | 8 | * struct dccp_hdr - generic part of DCCP packet header |
19 | * | 9 | * |
@@ -188,6 +178,9 @@ enum { | |||
188 | 178 | ||
189 | /* DCCP socket options */ | 179 | /* DCCP socket options */ |
190 | #define DCCP_SOCKOPT_PACKET_SIZE 1 | 180 | #define DCCP_SOCKOPT_PACKET_SIZE 1 |
181 | #define DCCP_SOCKOPT_SERVICE 2 | ||
182 | |||
183 | #define DCCP_SERVICE_LIST_MAX_LEN 32 | ||
191 | 184 | ||
192 | #ifdef __KERNEL__ | 185 | #ifdef __KERNEL__ |
193 | 186 | ||
@@ -382,6 +375,25 @@ enum dccp_role { | |||
382 | DCCP_ROLE_SERVER, | 375 | DCCP_ROLE_SERVER, |
383 | }; | 376 | }; |
384 | 377 | ||
378 | struct dccp_service_list { | ||
379 | __u32 dccpsl_nr; | ||
380 | __u32 dccpsl_list[0]; | ||
381 | }; | ||
382 | |||
383 | #define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1) | ||
384 | |||
385 | static inline int dccp_list_has_service(const struct dccp_service_list *sl, | ||
386 | const u32 service) | ||
387 | { | ||
388 | if (likely(sl != NULL)) { | ||
389 | u32 i = sl->dccpsl_nr; | ||
390 | while (i--) | ||
391 | if (sl->dccpsl_list[i] == service) | ||
392 | return 1; | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
385 | /** | 397 | /** |
386 | * struct dccp_sock - DCCP socket state | 398 | * struct dccp_sock - DCCP socket state |
387 | * | 399 | * |
@@ -417,7 +429,8 @@ struct dccp_sock { | |||
417 | __u64 dccps_gss; | 429 | __u64 dccps_gss; |
418 | __u64 dccps_gsr; | 430 | __u64 dccps_gsr; |
419 | __u64 dccps_gar; | 431 | __u64 dccps_gar; |
420 | unsigned long dccps_service; | 432 | __u32 dccps_service; |
433 | struct dccp_service_list *dccps_service_list; | ||
421 | struct timeval dccps_timestamp_time; | 434 | struct timeval dccps_timestamp_time; |
422 | __u32 dccps_timestamp_echo; | 435 | __u32 dccps_timestamp_echo; |
423 | __u32 dccps_packet_size; | 436 | __u32 dccps_packet_size; |
@@ -443,6 +456,11 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk) | |||
443 | return (struct dccp_sock *)sk; | 456 | return (struct dccp_sock *)sk; |
444 | } | 457 | } |
445 | 458 | ||
459 | static inline int dccp_service_not_initialized(const struct sock *sk) | ||
460 | { | ||
461 | return dccp_sk(sk)->dccps_service == DCCP_SERVICE_INVALID_VALUE; | ||
462 | } | ||
463 | |||
446 | static inline const char *dccp_role(const struct sock *sk) | 464 | static inline const char *dccp_role(const struct sock *sk) |
447 | { | 465 | { |
448 | switch (dccp_sk(sk)->dccps_role) { | 466 | switch (dccp_sk(sk)->dccps_role) { |
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 95c4630b3b18..be7a660b6b24 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h | |||
@@ -258,13 +258,12 @@ extern int dccp_v4_send_reset(struct sock *sk, | |||
258 | extern void dccp_send_close(struct sock *sk, const int active); | 258 | extern void dccp_send_close(struct sock *sk, const int active); |
259 | 259 | ||
260 | struct dccp_skb_cb { | 260 | struct dccp_skb_cb { |
261 | __u8 dccpd_type; | 261 | __u8 dccpd_type:4; |
262 | __u8 dccpd_reset_code; | 262 | __u8 dccpd_ccval:4; |
263 | __u8 dccpd_service; | 263 | __u8 dccpd_reset_code; |
264 | __u8 dccpd_ccval; | 264 | __u16 dccpd_opt_len; |
265 | __u64 dccpd_seq; | 265 | __u64 dccpd_seq; |
266 | __u64 dccpd_ack_seq; | 266 | __u64 dccpd_ack_seq; |
267 | int dccpd_opt_len; | ||
268 | }; | 267 | }; |
269 | 268 | ||
270 | #define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0])) | 269 | #define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0])) |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index e09907d8b7da..94a440b2685b 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -246,6 +246,9 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, | |||
246 | 246 | ||
247 | dp->dccps_role = DCCP_ROLE_CLIENT; | 247 | dp->dccps_role = DCCP_ROLE_CLIENT; |
248 | 248 | ||
249 | if (dccp_service_not_initialized(sk)) | ||
250 | return -EPROTO; | ||
251 | |||
249 | if (addr_len < sizeof(struct sockaddr_in)) | 252 | if (addr_len < sizeof(struct sockaddr_in)) |
250 | return -EINVAL; | 253 | return -EINVAL; |
251 | 254 | ||
@@ -661,6 +664,16 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk, | |||
661 | dccp_hdr(skb)->dccph_sport); | 664 | dccp_hdr(skb)->dccph_sport); |
662 | } | 665 | } |
663 | 666 | ||
667 | static inline int dccp_bad_service_code(const struct sock *sk, | ||
668 | const __u32 service) | ||
669 | { | ||
670 | const struct dccp_sock *dp = dccp_sk(sk); | ||
671 | |||
672 | if (dp->dccps_service == service) | ||
673 | return 0; | ||
674 | return !dccp_list_has_service(dp->dccps_service_list, service); | ||
675 | } | ||
676 | |||
664 | int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | 677 | int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) |
665 | { | 678 | { |
666 | struct inet_request_sock *ireq; | 679 | struct inet_request_sock *ireq; |
@@ -669,6 +682,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
669 | struct dccp_request_sock *dreq; | 682 | struct dccp_request_sock *dreq; |
670 | const __u32 saddr = skb->nh.iph->saddr; | 683 | const __u32 saddr = skb->nh.iph->saddr; |
671 | const __u32 daddr = skb->nh.iph->daddr; | 684 | const __u32 daddr = skb->nh.iph->daddr; |
685 | const __u32 service = dccp_hdr_request(skb)->dccph_req_service; | ||
672 | struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | 686 | struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); |
673 | __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY; | 687 | __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY; |
674 | struct dst_entry *dst = NULL; | 688 | struct dst_entry *dst = NULL; |
@@ -680,6 +694,10 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
680 | goto drop; | 694 | goto drop; |
681 | } | 695 | } |
682 | 696 | ||
697 | if (dccp_bad_service_code(sk, service)) { | ||
698 | reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE; | ||
699 | goto drop; | ||
700 | } | ||
683 | /* | 701 | /* |
684 | * TW buckets are converted to open requests without | 702 | * TW buckets are converted to open requests without |
685 | * limitations, they conserve resources and peer is | 703 | * limitations, they conserve resources and peer is |
@@ -722,9 +740,9 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
722 | * dccp_create_openreq_child. | 740 | * dccp_create_openreq_child. |
723 | */ | 741 | */ |
724 | dreq = dccp_rsk(req); | 742 | dreq = dccp_rsk(req); |
725 | dreq->dreq_isr = dcb->dccpd_seq; | 743 | dreq->dreq_isr = dcb->dccpd_seq; |
726 | dreq->dreq_iss = dccp_v4_init_sequence(sk, skb); | 744 | dreq->dreq_iss = dccp_v4_init_sequence(sk, skb); |
727 | dreq->dreq_service = dccp_hdr_request(skb)->dccph_req_service; | 745 | dreq->dreq_service = service; |
728 | 746 | ||
729 | if (dccp_v4_send_response(sk, req, dst)) | 747 | if (dccp_v4_send_response(sk, req, dst)) |
730 | goto drop_and_free; | 748 | goto drop_and_free; |
@@ -1284,6 +1302,7 @@ static int dccp_v4_init_sock(struct sock *sk) | |||
1284 | sk->sk_write_space = dccp_write_space; | 1302 | sk->sk_write_space = dccp_write_space; |
1285 | dp->dccps_mss_cache = 536; | 1303 | dp->dccps_mss_cache = 536; |
1286 | dp->dccps_role = DCCP_ROLE_UNDEFINED; | 1304 | dp->dccps_role = DCCP_ROLE_UNDEFINED; |
1305 | dp->dccps_service = DCCP_SERVICE_INVALID_VALUE; | ||
1287 | 1306 | ||
1288 | return 0; | 1307 | return 0; |
1289 | } | 1308 | } |
@@ -1305,6 +1324,11 @@ static int dccp_v4_destroy_sock(struct sock *sk) | |||
1305 | if (inet_csk(sk)->icsk_bind_hash != NULL) | 1324 | if (inet_csk(sk)->icsk_bind_hash != NULL) |
1306 | inet_put_port(&dccp_hashinfo, sk); | 1325 | inet_put_port(&dccp_hashinfo, sk); |
1307 | 1326 | ||
1327 | if (dp->dccps_service_list != NULL) { | ||
1328 | kfree(dp->dccps_service_list); | ||
1329 | dp->dccps_service_list = NULL; | ||
1330 | } | ||
1331 | |||
1308 | ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); | 1332 | ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); |
1309 | ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); | 1333 | ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); |
1310 | dccp_ackpkts_free(dp->dccps_hc_rx_ackpkts); | 1334 | dccp_ackpkts_free(dp->dccps_hc_rx_ackpkts); |
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 18461bc04cbe..933e10db1789 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c | |||
@@ -93,9 +93,11 @@ struct sock *dccp_create_openreq_child(struct sock *sk, | |||
93 | struct inet_connection_sock *newicsk = inet_csk(sk); | 93 | struct inet_connection_sock *newicsk = inet_csk(sk); |
94 | struct dccp_sock *newdp = dccp_sk(newsk); | 94 | struct dccp_sock *newdp = dccp_sk(newsk); |
95 | 95 | ||
96 | newdp->dccps_role = DCCP_ROLE_SERVER; | ||
96 | newdp->dccps_hc_rx_ackpkts = NULL; | 97 | newdp->dccps_hc_rx_ackpkts = NULL; |
97 | newdp->dccps_role = DCCP_ROLE_SERVER; | 98 | newdp->dccps_service_list = NULL; |
98 | newicsk->icsk_rto = DCCP_TIMEOUT_INIT; | 99 | newdp->dccps_service = dreq->dreq_service; |
100 | newicsk->icsk_rto = DCCP_TIMEOUT_INIT; | ||
99 | do_gettimeofday(&newdp->dccps_epoch); | 101 | do_gettimeofday(&newdp->dccps_epoch); |
100 | 102 | ||
101 | if (newdp->dccps_options.dccpo_send_ack_vector) { | 103 | if (newdp->dccps_options.dccpo_send_ack_vector) { |
diff --git a/net/dccp/output.c b/net/dccp/output.c index ea6d0e91e511..156b1d29a156 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c | |||
@@ -85,7 +85,7 @@ int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb) | |||
85 | switch (dcb->dccpd_type) { | 85 | switch (dcb->dccpd_type) { |
86 | case DCCP_PKT_REQUEST: | 86 | case DCCP_PKT_REQUEST: |
87 | dccp_hdr_request(skb)->dccph_req_service = | 87 | dccp_hdr_request(skb)->dccph_req_service = |
88 | dcb->dccpd_service; | 88 | dp->dccps_service; |
89 | break; | 89 | break; |
90 | case DCCP_PKT_RESET: | 90 | case DCCP_PKT_RESET: |
91 | dccp_hdr_reset(skb)->dccph_reset_code = | 91 | dccp_hdr_reset(skb)->dccph_reset_code = |
@@ -270,6 +270,7 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, | |||
270 | struct request_sock *req) | 270 | struct request_sock *req) |
271 | { | 271 | { |
272 | struct dccp_hdr *dh; | 272 | struct dccp_hdr *dh; |
273 | struct dccp_request_sock *dreq; | ||
273 | const int dccp_header_size = sizeof(struct dccp_hdr) + | 274 | const int dccp_header_size = sizeof(struct dccp_hdr) + |
274 | sizeof(struct dccp_hdr_ext) + | 275 | sizeof(struct dccp_hdr_ext) + |
275 | sizeof(struct dccp_hdr_response); | 276 | sizeof(struct dccp_hdr_response); |
@@ -285,8 +286,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, | |||
285 | skb->dst = dst_clone(dst); | 286 | skb->dst = dst_clone(dst); |
286 | skb->csum = 0; | 287 | skb->csum = 0; |
287 | 288 | ||
289 | dreq = dccp_rsk(req); | ||
288 | DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; | 290 | DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; |
289 | DCCP_SKB_CB(skb)->dccpd_seq = dccp_rsk(req)->dreq_iss; | 291 | DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss; |
290 | dccp_insert_options(sk, skb); | 292 | dccp_insert_options(sk, skb); |
291 | 293 | ||
292 | skb->h.raw = skb_push(skb, dccp_header_size); | 294 | skb->h.raw = skb_push(skb, dccp_header_size); |
@@ -300,8 +302,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, | |||
300 | DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; | 302 | DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; |
301 | dh->dccph_type = DCCP_PKT_RESPONSE; | 303 | dh->dccph_type = DCCP_PKT_RESPONSE; |
302 | dh->dccph_x = 1; | 304 | dh->dccph_x = 1; |
303 | dccp_hdr_set_seq(dh, dccp_rsk(req)->dreq_iss); | 305 | dccp_hdr_set_seq(dh, dreq->dreq_iss); |
304 | dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dccp_rsk(req)->dreq_isr); | 306 | dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr); |
307 | dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service; | ||
305 | 308 | ||
306 | dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr, | 309 | dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr, |
307 | inet_rsk(req)->rmt_addr); | 310 | inet_rsk(req)->rmt_addr); |
@@ -397,9 +400,6 @@ int dccp_connect(struct sock *sk) | |||
397 | skb_reserve(skb, MAX_DCCP_HEADER); | 400 | skb_reserve(skb, MAX_DCCP_HEADER); |
398 | 401 | ||
399 | DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST; | 402 | DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST; |
400 | /* FIXME: set service to something meaningful, coming | ||
401 | * from userspace*/ | ||
402 | DCCP_SKB_CB(skb)->dccpd_service = 0; | ||
403 | skb->csum = 0; | 403 | skb->csum = 0; |
404 | skb_set_owner_w(skb, sk); | 404 | skb_set_owner_w(skb, sk); |
405 | 405 | ||
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 18a0e69c9dc7..9bda2868eba6 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,6 +325,10 @@ 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 | ||
328 | if (optname == DCCP_SOCKOPT_SERVICE) | ||
329 | return dccp_getsockopt_service(sk, len, | ||
330 | (u32 __user *)optval, optlen); | ||
331 | |||
251 | len = min_t(unsigned int, len, sizeof(int)); | 332 | len = min_t(unsigned int, len, sizeof(int)); |
252 | if (len < 0) | 333 | if (len < 0) |
253 | return -EINVAL; | 334 | return -EINVAL; |