diff options
Diffstat (limited to 'net/dccp')
-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 |
5 files changed, 125 insertions, 19 deletions
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; |