diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2005-09-18 03:17:10 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-09-18 03:17:10 -0400 |
commit | 21f130a2370ba837cdfc5204ebe52e7c664fec3d (patch) | |
tree | 7f8a30088d8d39eab9350c59b6638661309ffe89 /net/dccp | |
parent | bc5e8fdfc622b03acf5ac974a1b8b26da6511c99 (diff) | |
parent | c58ec93245a1fb7354f9e331960380827b9f41db (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
Diffstat (limited to 'net/dccp')
-rw-r--r-- | net/dccp/dccp.h | 9 | ||||
-rw-r--r-- | net/dccp/input.c | 22 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 38 | ||||
-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, 142 insertions, 32 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/input.c b/net/dccp/input.c index c74034cf7ede..062e9f8359d0 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c | |||
@@ -384,9 +384,9 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, | |||
384 | } | 384 | } |
385 | 385 | ||
386 | out_invalid_packet: | 386 | out_invalid_packet: |
387 | return 1; /* dccp_v4_do_rcv will send a reset, but... | 387 | /* dccp_v4_do_rcv will send a reset */ |
388 | FIXME: the reset code should be | 388 | DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR; |
389 | DCCP_RESET_CODE_PACKET_ERROR */ | 389 | return 1; |
390 | } | 390 | } |
391 | 391 | ||
392 | static int dccp_rcv_respond_partopen_state_process(struct sock *sk, | 392 | static int dccp_rcv_respond_partopen_state_process(struct sock *sk, |
@@ -433,6 +433,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
433 | struct dccp_hdr *dh, unsigned len) | 433 | struct dccp_hdr *dh, unsigned len) |
434 | { | 434 | { |
435 | struct dccp_sock *dp = dccp_sk(sk); | 435 | struct dccp_sock *dp = dccp_sk(sk); |
436 | struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | ||
436 | const int old_state = sk->sk_state; | 437 | const int old_state = sk->sk_state; |
437 | int queued = 0; | 438 | int queued = 0; |
438 | 439 | ||
@@ -473,7 +474,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
473 | if (dh->dccph_type == DCCP_PKT_RESET) | 474 | if (dh->dccph_type == DCCP_PKT_RESET) |
474 | goto discard; | 475 | goto discard; |
475 | 476 | ||
476 | /* Caller (dccp_v4_do_rcv) will send Reset(No Connection)*/ | 477 | /* Caller (dccp_v4_do_rcv) will send Reset */ |
478 | dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; | ||
477 | return 1; | 479 | return 1; |
478 | } | 480 | } |
479 | 481 | ||
@@ -487,8 +489,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
487 | if (dccp_parse_options(sk, skb)) | 489 | if (dccp_parse_options(sk, skb)) |
488 | goto discard; | 490 | goto discard; |
489 | 491 | ||
490 | if (DCCP_SKB_CB(skb)->dccpd_ack_seq != | 492 | if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ) |
491 | DCCP_PKT_WITHOUT_ACK_SEQ) | ||
492 | dccp_event_ack_recv(sk, skb); | 493 | dccp_event_ack_recv(sk, skb); |
493 | 494 | ||
494 | ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb); | 495 | ccid_hc_rx_packet_recv(dp->dccps_hc_rx_ccid, sk, skb); |
@@ -500,7 +501,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
500 | */ | 501 | */ |
501 | if (dp->dccps_options.dccpo_send_ack_vector) { | 502 | if (dp->dccps_options.dccpo_send_ack_vector) { |
502 | if (dccp_ackpkts_add(dp->dccps_hc_rx_ackpkts, sk, | 503 | if (dccp_ackpkts_add(dp->dccps_hc_rx_ackpkts, sk, |
503 | DCCP_SKB_CB(skb)->dccpd_seq, | 504 | dcb->dccpd_seq, |
504 | DCCP_ACKPKTS_STATE_RECEIVED)) | 505 | DCCP_ACKPKTS_STATE_RECEIVED)) |
505 | goto discard; | 506 | goto discard; |
506 | /* | 507 | /* |
@@ -551,8 +552,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
551 | dh->dccph_type == DCCP_PKT_REQUEST) || | 552 | dh->dccph_type == DCCP_PKT_REQUEST) || |
552 | (sk->sk_state == DCCP_RESPOND && | 553 | (sk->sk_state == DCCP_RESPOND && |
553 | dh->dccph_type == DCCP_PKT_DATA)) { | 554 | dh->dccph_type == DCCP_PKT_DATA)) { |
554 | dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq, | 555 | dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNC); |
555 | DCCP_PKT_SYNC); | ||
556 | goto discard; | 556 | goto discard; |
557 | } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { | 557 | } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { |
558 | dccp_rcv_closereq(sk, skb); | 558 | dccp_rcv_closereq(sk, skb); |
@@ -563,13 +563,13 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, | |||
563 | } | 563 | } |
564 | 564 | ||
565 | if (unlikely(dh->dccph_type == DCCP_PKT_SYNC)) { | 565 | if (unlikely(dh->dccph_type == DCCP_PKT_SYNC)) { |
566 | dccp_send_sync(sk, DCCP_SKB_CB(skb)->dccpd_seq, | 566 | dccp_send_sync(sk, dcb->dccpd_seq, DCCP_PKT_SYNCACK); |
567 | DCCP_PKT_SYNCACK); | ||
568 | goto discard; | 567 | goto discard; |
569 | } | 568 | } |
570 | 569 | ||
571 | switch (sk->sk_state) { | 570 | switch (sk->sk_state) { |
572 | case DCCP_CLOSED: | 571 | case DCCP_CLOSED: |
572 | dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; | ||
573 | return 1; | 573 | return 1; |
574 | 574 | ||
575 | case DCCP_REQUESTING: | 575 | case DCCP_REQUESTING: |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 2afaa464e7f0..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,13 +682,22 @@ 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; | ||
686 | struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | ||
687 | __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY; | ||
672 | struct dst_entry *dst = NULL; | 688 | struct dst_entry *dst = NULL; |
673 | 689 | ||
674 | /* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */ | 690 | /* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */ |
675 | if (((struct rtable *)skb->dst)->rt_flags & | 691 | if (((struct rtable *)skb->dst)->rt_flags & |
676 | (RTCF_BROADCAST | RTCF_MULTICAST)) | 692 | (RTCF_BROADCAST | RTCF_MULTICAST)) { |
693 | reset_code = DCCP_RESET_CODE_NO_CONNECTION; | ||
677 | goto drop; | 694 | goto drop; |
695 | } | ||
678 | 696 | ||
697 | if (dccp_bad_service_code(sk, service)) { | ||
698 | reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE; | ||
699 | goto drop; | ||
700 | } | ||
679 | /* | 701 | /* |
680 | * TW buckets are converted to open requests without | 702 | * TW buckets are converted to open requests without |
681 | * limitations, they conserve resources and peer is | 703 | * limitations, they conserve resources and peer is |
@@ -718,9 +740,9 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | |||
718 | * dccp_create_openreq_child. | 740 | * dccp_create_openreq_child. |
719 | */ | 741 | */ |
720 | dreq = dccp_rsk(req); | 742 | dreq = dccp_rsk(req); |
721 | dreq->dreq_isr = DCCP_SKB_CB(skb)->dccpd_seq; | 743 | dreq->dreq_isr = dcb->dccpd_seq; |
722 | dreq->dreq_iss = dccp_v4_init_sequence(sk, skb); | 744 | dreq->dreq_iss = dccp_v4_init_sequence(sk, skb); |
723 | dreq->dreq_service = dccp_hdr_request(skb)->dccph_req_service; | 745 | dreq->dreq_service = service; |
724 | 746 | ||
725 | if (dccp_v4_send_response(sk, req, dst)) | 747 | if (dccp_v4_send_response(sk, req, dst)) |
726 | goto drop_and_free; | 748 | goto drop_and_free; |
@@ -735,6 +757,7 @@ drop_and_free: | |||
735 | __reqsk_free(req); | 757 | __reqsk_free(req); |
736 | drop: | 758 | drop: |
737 | DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS); | 759 | DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS); |
760 | dcb->dccpd_reset_code = reset_code; | ||
738 | return -1; | 761 | return -1; |
739 | } | 762 | } |
740 | 763 | ||
@@ -1005,7 +1028,6 @@ int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
1005 | return 0; | 1028 | return 0; |
1006 | 1029 | ||
1007 | reset: | 1030 | reset: |
1008 | DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; | ||
1009 | dccp_v4_ctl_send_reset(skb); | 1031 | dccp_v4_ctl_send_reset(skb); |
1010 | discard: | 1032 | discard: |
1011 | kfree_skb(skb); | 1033 | kfree_skb(skb); |
@@ -1280,6 +1302,7 @@ static int dccp_v4_init_sock(struct sock *sk) | |||
1280 | sk->sk_write_space = dccp_write_space; | 1302 | sk->sk_write_space = dccp_write_space; |
1281 | dp->dccps_mss_cache = 536; | 1303 | dp->dccps_mss_cache = 536; |
1282 | dp->dccps_role = DCCP_ROLE_UNDEFINED; | 1304 | dp->dccps_role = DCCP_ROLE_UNDEFINED; |
1305 | dp->dccps_service = DCCP_SERVICE_INVALID_VALUE; | ||
1283 | 1306 | ||
1284 | return 0; | 1307 | return 0; |
1285 | } | 1308 | } |
@@ -1301,6 +1324,11 @@ static int dccp_v4_destroy_sock(struct sock *sk) | |||
1301 | if (inet_csk(sk)->icsk_bind_hash != NULL) | 1324 | if (inet_csk(sk)->icsk_bind_hash != NULL) |
1302 | inet_put_port(&dccp_hashinfo, sk); | 1325 | inet_put_port(&dccp_hashinfo, sk); |
1303 | 1326 | ||
1327 | if (dp->dccps_service_list != NULL) { | ||
1328 | kfree(dp->dccps_service_list); | ||
1329 | dp->dccps_service_list = NULL; | ||
1330 | } | ||
1331 | |||
1304 | ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); | 1332 | ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); |
1305 | ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); | 1333 | ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); |
1306 | 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; |