diff options
Diffstat (limited to 'net/dccp/proto.c')
-rw-r--r-- | net/dccp/proto.c | 440 |
1 files changed, 301 insertions, 139 deletions
diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 65b11ea90d85..d4b293e16283 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c | |||
@@ -23,9 +23,7 @@ | |||
23 | #include <linux/random.h> | 23 | #include <linux/random.h> |
24 | #include <net/checksum.h> | 24 | #include <net/checksum.h> |
25 | 25 | ||
26 | #include <net/inet_common.h> | ||
27 | #include <net/inet_sock.h> | 26 | #include <net/inet_sock.h> |
28 | #include <net/protocol.h> | ||
29 | #include <net/sock.h> | 27 | #include <net/sock.h> |
30 | #include <net/xfrm.h> | 28 | #include <net/xfrm.h> |
31 | 29 | ||
@@ -37,6 +35,7 @@ | |||
37 | 35 | ||
38 | #include "ccid.h" | 36 | #include "ccid.h" |
39 | #include "dccp.h" | 37 | #include "dccp.h" |
38 | #include "feat.h" | ||
40 | 39 | ||
41 | DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; | 40 | DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly; |
42 | 41 | ||
@@ -46,12 +45,66 @@ atomic_t dccp_orphan_count = ATOMIC_INIT(0); | |||
46 | 45 | ||
47 | EXPORT_SYMBOL_GPL(dccp_orphan_count); | 46 | EXPORT_SYMBOL_GPL(dccp_orphan_count); |
48 | 47 | ||
49 | static struct net_protocol dccp_protocol = { | 48 | struct inet_hashinfo __cacheline_aligned dccp_hashinfo = { |
50 | .handler = dccp_v4_rcv, | 49 | .lhash_lock = RW_LOCK_UNLOCKED, |
51 | .err_handler = dccp_v4_err, | 50 | .lhash_users = ATOMIC_INIT(0), |
52 | .no_policy = 1, | 51 | .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait), |
53 | }; | 52 | }; |
54 | 53 | ||
54 | EXPORT_SYMBOL_GPL(dccp_hashinfo); | ||
55 | |||
56 | void dccp_set_state(struct sock *sk, const int state) | ||
57 | { | ||
58 | const int oldstate = sk->sk_state; | ||
59 | |||
60 | dccp_pr_debug("%s(%p) %-10.10s -> %s\n", | ||
61 | dccp_role(sk), sk, | ||
62 | dccp_state_name(oldstate), dccp_state_name(state)); | ||
63 | WARN_ON(state == oldstate); | ||
64 | |||
65 | switch (state) { | ||
66 | case DCCP_OPEN: | ||
67 | if (oldstate != DCCP_OPEN) | ||
68 | DCCP_INC_STATS(DCCP_MIB_CURRESTAB); | ||
69 | break; | ||
70 | |||
71 | case DCCP_CLOSED: | ||
72 | if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN) | ||
73 | DCCP_INC_STATS(DCCP_MIB_ESTABRESETS); | ||
74 | |||
75 | sk->sk_prot->unhash(sk); | ||
76 | if (inet_csk(sk)->icsk_bind_hash != NULL && | ||
77 | !(sk->sk_userlocks & SOCK_BINDPORT_LOCK)) | ||
78 | inet_put_port(&dccp_hashinfo, sk); | ||
79 | /* fall through */ | ||
80 | default: | ||
81 | if (oldstate == DCCP_OPEN) | ||
82 | DCCP_DEC_STATS(DCCP_MIB_CURRESTAB); | ||
83 | } | ||
84 | |||
85 | /* Change state AFTER socket is unhashed to avoid closed | ||
86 | * socket sitting in hash tables. | ||
87 | */ | ||
88 | sk->sk_state = state; | ||
89 | } | ||
90 | |||
91 | EXPORT_SYMBOL_GPL(dccp_set_state); | ||
92 | |||
93 | void dccp_done(struct sock *sk) | ||
94 | { | ||
95 | dccp_set_state(sk, DCCP_CLOSED); | ||
96 | dccp_clear_xmit_timers(sk); | ||
97 | |||
98 | sk->sk_shutdown = SHUTDOWN_MASK; | ||
99 | |||
100 | if (!sock_flag(sk, SOCK_DEAD)) | ||
101 | sk->sk_state_change(sk); | ||
102 | else | ||
103 | inet_csk_destroy_sock(sk); | ||
104 | } | ||
105 | |||
106 | EXPORT_SYMBOL_GPL(dccp_done); | ||
107 | |||
55 | const char *dccp_packet_name(const int type) | 108 | const char *dccp_packet_name(const int type) |
56 | { | 109 | { |
57 | static const char *dccp_packet_names[] = { | 110 | static const char *dccp_packet_names[] = { |
@@ -96,6 +149,120 @@ const char *dccp_state_name(const int state) | |||
96 | 149 | ||
97 | EXPORT_SYMBOL_GPL(dccp_state_name); | 150 | EXPORT_SYMBOL_GPL(dccp_state_name); |
98 | 151 | ||
152 | void dccp_hash(struct sock *sk) | ||
153 | { | ||
154 | inet_hash(&dccp_hashinfo, sk); | ||
155 | } | ||
156 | |||
157 | EXPORT_SYMBOL_GPL(dccp_hash); | ||
158 | |||
159 | void dccp_unhash(struct sock *sk) | ||
160 | { | ||
161 | inet_unhash(&dccp_hashinfo, sk); | ||
162 | } | ||
163 | |||
164 | EXPORT_SYMBOL_GPL(dccp_unhash); | ||
165 | |||
166 | int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) | ||
167 | { | ||
168 | struct dccp_sock *dp = dccp_sk(sk); | ||
169 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
170 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
171 | |||
172 | dccp_minisock_init(&dp->dccps_minisock); | ||
173 | do_gettimeofday(&dp->dccps_epoch); | ||
174 | |||
175 | /* | ||
176 | * FIXME: We're hardcoding the CCID, and doing this at this point makes | ||
177 | * the listening (master) sock get CCID control blocks, which is not | ||
178 | * necessary, but for now, to not mess with the test userspace apps, | ||
179 | * lets leave it here, later the real solution is to do this in a | ||
180 | * setsockopt(CCIDs-I-want/accept). -acme | ||
181 | */ | ||
182 | if (likely(ctl_sock_initialized)) { | ||
183 | int rc = dccp_feat_init(dmsk); | ||
184 | |||
185 | if (rc) | ||
186 | return rc; | ||
187 | |||
188 | if (dmsk->dccpms_send_ack_vector) { | ||
189 | dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL); | ||
190 | if (dp->dccps_hc_rx_ackvec == NULL) | ||
191 | return -ENOMEM; | ||
192 | } | ||
193 | dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid, | ||
194 | sk, GFP_KERNEL); | ||
195 | dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid, | ||
196 | sk, GFP_KERNEL); | ||
197 | if (unlikely(dp->dccps_hc_rx_ccid == NULL || | ||
198 | dp->dccps_hc_tx_ccid == NULL)) { | ||
199 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
200 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
201 | if (dmsk->dccpms_send_ack_vector) { | ||
202 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | ||
203 | dp->dccps_hc_rx_ackvec = NULL; | ||
204 | } | ||
205 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | ||
206 | return -ENOMEM; | ||
207 | } | ||
208 | } else { | ||
209 | /* control socket doesn't need feat nego */ | ||
210 | INIT_LIST_HEAD(&dmsk->dccpms_pending); | ||
211 | INIT_LIST_HEAD(&dmsk->dccpms_conf); | ||
212 | } | ||
213 | |||
214 | dccp_init_xmit_timers(sk); | ||
215 | icsk->icsk_rto = DCCP_TIMEOUT_INIT; | ||
216 | sk->sk_state = DCCP_CLOSED; | ||
217 | sk->sk_write_space = dccp_write_space; | ||
218 | icsk->icsk_sync_mss = dccp_sync_mss; | ||
219 | dp->dccps_mss_cache = 536; | ||
220 | dp->dccps_role = DCCP_ROLE_UNDEFINED; | ||
221 | dp->dccps_service = DCCP_SERVICE_INVALID_VALUE; | ||
222 | dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1; | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | EXPORT_SYMBOL_GPL(dccp_init_sock); | ||
228 | |||
229 | int dccp_destroy_sock(struct sock *sk) | ||
230 | { | ||
231 | struct dccp_sock *dp = dccp_sk(sk); | ||
232 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
233 | |||
234 | /* | ||
235 | * DCCP doesn't use sk_write_queue, just sk_send_head | ||
236 | * for retransmissions | ||
237 | */ | ||
238 | if (sk->sk_send_head != NULL) { | ||
239 | kfree_skb(sk->sk_send_head); | ||
240 | sk->sk_send_head = NULL; | ||
241 | } | ||
242 | |||
243 | /* Clean up a referenced DCCP bind bucket. */ | ||
244 | if (inet_csk(sk)->icsk_bind_hash != NULL) | ||
245 | inet_put_port(&dccp_hashinfo, sk); | ||
246 | |||
247 | kfree(dp->dccps_service_list); | ||
248 | dp->dccps_service_list = NULL; | ||
249 | |||
250 | if (dmsk->dccpms_send_ack_vector) { | ||
251 | dccp_ackvec_free(dp->dccps_hc_rx_ackvec); | ||
252 | dp->dccps_hc_rx_ackvec = NULL; | ||
253 | } | ||
254 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
255 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
256 | dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL; | ||
257 | |||
258 | /* clean up feature negotiation state */ | ||
259 | dccp_feat_clean(dmsk); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | EXPORT_SYMBOL_GPL(dccp_destroy_sock); | ||
265 | |||
99 | static inline int dccp_listen_start(struct sock *sk) | 266 | static inline int dccp_listen_start(struct sock *sk) |
100 | { | 267 | { |
101 | struct dccp_sock *dp = dccp_sk(sk); | 268 | struct dccp_sock *dp = dccp_sk(sk); |
@@ -220,7 +387,7 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) | |||
220 | 387 | ||
221 | EXPORT_SYMBOL_GPL(dccp_ioctl); | 388 | EXPORT_SYMBOL_GPL(dccp_ioctl); |
222 | 389 | ||
223 | static int dccp_setsockopt_service(struct sock *sk, const u32 service, | 390 | static int dccp_setsockopt_service(struct sock *sk, const __be32 service, |
224 | char __user *optval, int optlen) | 391 | char __user *optval, int optlen) |
225 | { | 392 | { |
226 | struct dccp_sock *dp = dccp_sk(sk); | 393 | struct dccp_sock *dp = dccp_sk(sk); |
@@ -255,18 +422,46 @@ static int dccp_setsockopt_service(struct sock *sk, const u32 service, | |||
255 | return 0; | 422 | return 0; |
256 | } | 423 | } |
257 | 424 | ||
258 | int dccp_setsockopt(struct sock *sk, int level, int optname, | 425 | /* byte 1 is feature. the rest is the preference list */ |
259 | char __user *optval, int optlen) | 426 | static int dccp_setsockopt_change(struct sock *sk, int type, |
427 | struct dccp_so_feat __user *optval) | ||
428 | { | ||
429 | struct dccp_so_feat opt; | ||
430 | u8 *val; | ||
431 | int rc; | ||
432 | |||
433 | if (copy_from_user(&opt, optval, sizeof(opt))) | ||
434 | return -EFAULT; | ||
435 | |||
436 | val = kmalloc(opt.dccpsf_len, GFP_KERNEL); | ||
437 | if (!val) | ||
438 | return -ENOMEM; | ||
439 | |||
440 | if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) { | ||
441 | rc = -EFAULT; | ||
442 | goto out_free_val; | ||
443 | } | ||
444 | |||
445 | rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat, | ||
446 | val, opt.dccpsf_len, GFP_KERNEL); | ||
447 | if (rc) | ||
448 | goto out_free_val; | ||
449 | |||
450 | out: | ||
451 | return rc; | ||
452 | |||
453 | out_free_val: | ||
454 | kfree(val); | ||
455 | goto out; | ||
456 | } | ||
457 | |||
458 | static int do_dccp_setsockopt(struct sock *sk, int level, int optname, | ||
459 | char __user *optval, int optlen) | ||
260 | { | 460 | { |
261 | struct dccp_sock *dp; | 461 | struct dccp_sock *dp; |
262 | int err; | 462 | int err; |
263 | int val; | 463 | int val; |
264 | 464 | ||
265 | if (level != SOL_DCCP) | ||
266 | return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, | ||
267 | optname, optval, | ||
268 | optlen); | ||
269 | |||
270 | if (optlen < sizeof(int)) | 465 | if (optlen < sizeof(int)) |
271 | return -EINVAL; | 466 | return -EINVAL; |
272 | 467 | ||
@@ -284,6 +479,25 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, | |||
284 | case DCCP_SOCKOPT_PACKET_SIZE: | 479 | case DCCP_SOCKOPT_PACKET_SIZE: |
285 | dp->dccps_packet_size = val; | 480 | dp->dccps_packet_size = val; |
286 | break; | 481 | break; |
482 | |||
483 | case DCCP_SOCKOPT_CHANGE_L: | ||
484 | if (optlen != sizeof(struct dccp_so_feat)) | ||
485 | err = -EINVAL; | ||
486 | else | ||
487 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L, | ||
488 | (struct dccp_so_feat *) | ||
489 | optval); | ||
490 | break; | ||
491 | |||
492 | case DCCP_SOCKOPT_CHANGE_R: | ||
493 | if (optlen != sizeof(struct dccp_so_feat)) | ||
494 | err = -EINVAL; | ||
495 | else | ||
496 | err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R, | ||
497 | (struct dccp_so_feat *) | ||
498 | optval); | ||
499 | break; | ||
500 | |||
287 | default: | 501 | default: |
288 | err = -ENOPROTOOPT; | 502 | err = -ENOPROTOOPT; |
289 | break; | 503 | break; |
@@ -293,10 +507,33 @@ int dccp_setsockopt(struct sock *sk, int level, int optname, | |||
293 | return err; | 507 | return err; |
294 | } | 508 | } |
295 | 509 | ||
510 | int dccp_setsockopt(struct sock *sk, int level, int optname, | ||
511 | char __user *optval, int optlen) | ||
512 | { | ||
513 | if (level != SOL_DCCP) | ||
514 | return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level, | ||
515 | optname, optval, | ||
516 | optlen); | ||
517 | return do_dccp_setsockopt(sk, level, optname, optval, optlen); | ||
518 | } | ||
519 | |||
296 | EXPORT_SYMBOL_GPL(dccp_setsockopt); | 520 | EXPORT_SYMBOL_GPL(dccp_setsockopt); |
297 | 521 | ||
522 | #ifdef CONFIG_COMPAT | ||
523 | int compat_dccp_setsockopt(struct sock *sk, int level, int optname, | ||
524 | char __user *optval, int optlen) | ||
525 | { | ||
526 | if (level != SOL_DCCP) | ||
527 | return inet_csk_compat_setsockopt(sk, level, optname, | ||
528 | optval, optlen); | ||
529 | return do_dccp_setsockopt(sk, level, optname, optval, optlen); | ||
530 | } | ||
531 | |||
532 | EXPORT_SYMBOL_GPL(compat_dccp_setsockopt); | ||
533 | #endif | ||
534 | |||
298 | static int dccp_getsockopt_service(struct sock *sk, int len, | 535 | static int dccp_getsockopt_service(struct sock *sk, int len, |
299 | u32 __user *optval, | 536 | __be32 __user *optval, |
300 | int __user *optlen) | 537 | int __user *optlen) |
301 | { | 538 | { |
302 | const struct dccp_sock *dp = dccp_sk(sk); | 539 | const struct dccp_sock *dp = dccp_sk(sk); |
@@ -326,16 +563,12 @@ out: | |||
326 | return err; | 563 | return err; |
327 | } | 564 | } |
328 | 565 | ||
329 | int dccp_getsockopt(struct sock *sk, int level, int optname, | 566 | static int do_dccp_getsockopt(struct sock *sk, int level, int optname, |
330 | char __user *optval, int __user *optlen) | 567 | char __user *optval, int __user *optlen) |
331 | { | 568 | { |
332 | struct dccp_sock *dp; | 569 | struct dccp_sock *dp; |
333 | int val, len; | 570 | int val, len; |
334 | 571 | ||
335 | if (level != SOL_DCCP) | ||
336 | return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, | ||
337 | optname, optval, | ||
338 | optlen); | ||
339 | if (get_user(len, optlen)) | 572 | if (get_user(len, optlen)) |
340 | return -EFAULT; | 573 | return -EFAULT; |
341 | 574 | ||
@@ -351,7 +584,7 @@ int dccp_getsockopt(struct sock *sk, int level, int optname, | |||
351 | break; | 584 | break; |
352 | case DCCP_SOCKOPT_SERVICE: | 585 | case DCCP_SOCKOPT_SERVICE: |
353 | return dccp_getsockopt_service(sk, len, | 586 | return dccp_getsockopt_service(sk, len, |
354 | (u32 __user *)optval, optlen); | 587 | (__be32 __user *)optval, optlen); |
355 | case 128 ... 191: | 588 | case 128 ... 191: |
356 | return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, | 589 | return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname, |
357 | len, (u32 __user *)optval, optlen); | 590 | len, (u32 __user *)optval, optlen); |
@@ -368,8 +601,31 @@ int dccp_getsockopt(struct sock *sk, int level, int optname, | |||
368 | return 0; | 601 | return 0; |
369 | } | 602 | } |
370 | 603 | ||
604 | int dccp_getsockopt(struct sock *sk, int level, int optname, | ||
605 | char __user *optval, int __user *optlen) | ||
606 | { | ||
607 | if (level != SOL_DCCP) | ||
608 | return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level, | ||
609 | optname, optval, | ||
610 | optlen); | ||
611 | return do_dccp_getsockopt(sk, level, optname, optval, optlen); | ||
612 | } | ||
613 | |||
371 | EXPORT_SYMBOL_GPL(dccp_getsockopt); | 614 | EXPORT_SYMBOL_GPL(dccp_getsockopt); |
372 | 615 | ||
616 | #ifdef CONFIG_COMPAT | ||
617 | int compat_dccp_getsockopt(struct sock *sk, int level, int optname, | ||
618 | char __user *optval, int __user *optlen) | ||
619 | { | ||
620 | if (level != SOL_DCCP) | ||
621 | return inet_csk_compat_getsockopt(sk, level, optname, | ||
622 | optval, optlen); | ||
623 | return do_dccp_getsockopt(sk, level, optname, optval, optlen); | ||
624 | } | ||
625 | |||
626 | EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); | ||
627 | #endif | ||
628 | |||
373 | int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 629 | int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, |
374 | size_t len) | 630 | size_t len) |
375 | { | 631 | { |
@@ -679,84 +935,7 @@ void dccp_shutdown(struct sock *sk, int how) | |||
679 | 935 | ||
680 | EXPORT_SYMBOL_GPL(dccp_shutdown); | 936 | EXPORT_SYMBOL_GPL(dccp_shutdown); |
681 | 937 | ||
682 | static const struct proto_ops inet_dccp_ops = { | 938 | static int __init dccp_mib_init(void) |
683 | .family = PF_INET, | ||
684 | .owner = THIS_MODULE, | ||
685 | .release = inet_release, | ||
686 | .bind = inet_bind, | ||
687 | .connect = inet_stream_connect, | ||
688 | .socketpair = sock_no_socketpair, | ||
689 | .accept = inet_accept, | ||
690 | .getname = inet_getname, | ||
691 | /* FIXME: work on tcp_poll to rename it to inet_csk_poll */ | ||
692 | .poll = dccp_poll, | ||
693 | .ioctl = inet_ioctl, | ||
694 | /* FIXME: work on inet_listen to rename it to sock_common_listen */ | ||
695 | .listen = inet_dccp_listen, | ||
696 | .shutdown = inet_shutdown, | ||
697 | .setsockopt = sock_common_setsockopt, | ||
698 | .getsockopt = sock_common_getsockopt, | ||
699 | .sendmsg = inet_sendmsg, | ||
700 | .recvmsg = sock_common_recvmsg, | ||
701 | .mmap = sock_no_mmap, | ||
702 | .sendpage = sock_no_sendpage, | ||
703 | }; | ||
704 | |||
705 | extern struct net_proto_family inet_family_ops; | ||
706 | |||
707 | static struct inet_protosw dccp_v4_protosw = { | ||
708 | .type = SOCK_DCCP, | ||
709 | .protocol = IPPROTO_DCCP, | ||
710 | .prot = &dccp_prot, | ||
711 | .ops = &inet_dccp_ops, | ||
712 | .capability = -1, | ||
713 | .no_check = 0, | ||
714 | .flags = INET_PROTOSW_ICSK, | ||
715 | }; | ||
716 | |||
717 | /* | ||
718 | * This is the global socket data structure used for responding to | ||
719 | * the Out-of-the-blue (OOTB) packets. A control sock will be created | ||
720 | * for this socket at the initialization time. | ||
721 | */ | ||
722 | struct socket *dccp_ctl_socket; | ||
723 | |||
724 | static char dccp_ctl_socket_err_msg[] __initdata = | ||
725 | KERN_ERR "DCCP: Failed to create the control socket.\n"; | ||
726 | |||
727 | static int __init dccp_ctl_sock_init(void) | ||
728 | { | ||
729 | int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP, | ||
730 | &dccp_ctl_socket); | ||
731 | if (rc < 0) | ||
732 | printk(dccp_ctl_socket_err_msg); | ||
733 | else { | ||
734 | dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC; | ||
735 | inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1; | ||
736 | |||
737 | /* Unhash it so that IP input processing does not even | ||
738 | * see it, we do not wish this socket to see incoming | ||
739 | * packets. | ||
740 | */ | ||
741 | dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk); | ||
742 | } | ||
743 | |||
744 | return rc; | ||
745 | } | ||
746 | |||
747 | #ifdef CONFIG_IP_DCCP_UNLOAD_HACK | ||
748 | void dccp_ctl_sock_exit(void) | ||
749 | { | ||
750 | if (dccp_ctl_socket != NULL) { | ||
751 | sock_release(dccp_ctl_socket); | ||
752 | dccp_ctl_socket = NULL; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit); | ||
757 | #endif | ||
758 | |||
759 | static int __init init_dccp_v4_mibs(void) | ||
760 | { | 939 | { |
761 | int rc = -ENOMEM; | 940 | int rc = -ENOMEM; |
762 | 941 | ||
@@ -778,6 +957,13 @@ out_free_one: | |||
778 | 957 | ||
779 | } | 958 | } |
780 | 959 | ||
960 | static void dccp_mib_exit(void) | ||
961 | { | ||
962 | free_percpu(dccp_statistics[0]); | ||
963 | free_percpu(dccp_statistics[1]); | ||
964 | dccp_statistics[0] = dccp_statistics[1] = NULL; | ||
965 | } | ||
966 | |||
781 | static int thash_entries; | 967 | static int thash_entries; |
782 | module_param(thash_entries, int, 0444); | 968 | module_param(thash_entries, int, 0444); |
783 | MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); | 969 | MODULE_PARM_DESC(thash_entries, "Number of ehash buckets"); |
@@ -794,17 +980,14 @@ static int __init dccp_init(void) | |||
794 | { | 980 | { |
795 | unsigned long goal; | 981 | unsigned long goal; |
796 | int ehash_order, bhash_order, i; | 982 | int ehash_order, bhash_order, i; |
797 | int rc = proto_register(&dccp_prot, 1); | 983 | int rc = -ENOBUFS; |
798 | |||
799 | if (rc) | ||
800 | goto out; | ||
801 | 984 | ||
802 | dccp_hashinfo.bind_bucket_cachep = | 985 | dccp_hashinfo.bind_bucket_cachep = |
803 | kmem_cache_create("dccp_bind_bucket", | 986 | kmem_cache_create("dccp_bind_bucket", |
804 | sizeof(struct inet_bind_bucket), 0, | 987 | sizeof(struct inet_bind_bucket), 0, |
805 | SLAB_HWCACHE_ALIGN, NULL, NULL); | 988 | SLAB_HWCACHE_ALIGN, NULL, NULL); |
806 | if (!dccp_hashinfo.bind_bucket_cachep) | 989 | if (!dccp_hashinfo.bind_bucket_cachep) |
807 | goto out_proto_unregister; | 990 | goto out; |
808 | 991 | ||
809 | /* | 992 | /* |
810 | * Size and allocate the main established and bind bucket | 993 | * Size and allocate the main established and bind bucket |
@@ -866,27 +1049,23 @@ static int __init dccp_init(void) | |||
866 | INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); | 1049 | INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain); |
867 | } | 1050 | } |
868 | 1051 | ||
869 | if (init_dccp_v4_mibs()) | 1052 | rc = dccp_mib_init(); |
1053 | if (rc) | ||
870 | goto out_free_dccp_bhash; | 1054 | goto out_free_dccp_bhash; |
871 | 1055 | ||
872 | rc = -EAGAIN; | 1056 | rc = dccp_ackvec_init(); |
873 | if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP)) | 1057 | if (rc) |
874 | goto out_free_dccp_v4_mibs; | 1058 | goto out_free_dccp_mib; |
875 | |||
876 | inet_register_protosw(&dccp_v4_protosw); | ||
877 | 1059 | ||
878 | rc = dccp_ctl_sock_init(); | 1060 | rc = dccp_sysctl_init(); |
879 | if (rc) | 1061 | if (rc) |
880 | goto out_unregister_protosw; | 1062 | goto out_ackvec_exit; |
881 | out: | 1063 | out: |
882 | return rc; | 1064 | return rc; |
883 | out_unregister_protosw: | 1065 | out_ackvec_exit: |
884 | inet_unregister_protosw(&dccp_v4_protosw); | 1066 | dccp_ackvec_exit(); |
885 | inet_del_protocol(&dccp_protocol, IPPROTO_DCCP); | 1067 | out_free_dccp_mib: |
886 | out_free_dccp_v4_mibs: | 1068 | dccp_mib_exit(); |
887 | free_percpu(dccp_statistics[0]); | ||
888 | free_percpu(dccp_statistics[1]); | ||
889 | dccp_statistics[0] = dccp_statistics[1] = NULL; | ||
890 | out_free_dccp_bhash: | 1069 | out_free_dccp_bhash: |
891 | free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); | 1070 | free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); |
892 | dccp_hashinfo.bhash = NULL; | 1071 | dccp_hashinfo.bhash = NULL; |
@@ -896,23 +1075,12 @@ out_free_dccp_ehash: | |||
896 | out_free_bind_bucket_cachep: | 1075 | out_free_bind_bucket_cachep: |
897 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); | 1076 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
898 | dccp_hashinfo.bind_bucket_cachep = NULL; | 1077 | dccp_hashinfo.bind_bucket_cachep = NULL; |
899 | out_proto_unregister: | ||
900 | proto_unregister(&dccp_prot); | ||
901 | goto out; | 1078 | goto out; |
902 | } | 1079 | } |
903 | 1080 | ||
904 | static const char dccp_del_proto_err_msg[] __exitdata = | ||
905 | KERN_ERR "can't remove dccp net_protocol\n"; | ||
906 | |||
907 | static void __exit dccp_fini(void) | 1081 | static void __exit dccp_fini(void) |
908 | { | 1082 | { |
909 | inet_unregister_protosw(&dccp_v4_protosw); | 1083 | dccp_mib_exit(); |
910 | |||
911 | if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0) | ||
912 | printk(dccp_del_proto_err_msg); | ||
913 | |||
914 | free_percpu(dccp_statistics[0]); | ||
915 | free_percpu(dccp_statistics[1]); | ||
916 | free_pages((unsigned long)dccp_hashinfo.bhash, | 1084 | free_pages((unsigned long)dccp_hashinfo.bhash, |
917 | get_order(dccp_hashinfo.bhash_size * | 1085 | get_order(dccp_hashinfo.bhash_size * |
918 | sizeof(struct inet_bind_hashbucket))); | 1086 | sizeof(struct inet_bind_hashbucket))); |
@@ -920,19 +1088,13 @@ static void __exit dccp_fini(void) | |||
920 | get_order(dccp_hashinfo.ehash_size * | 1088 | get_order(dccp_hashinfo.ehash_size * |
921 | sizeof(struct inet_ehash_bucket))); | 1089 | sizeof(struct inet_ehash_bucket))); |
922 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); | 1090 | kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); |
923 | proto_unregister(&dccp_prot); | 1091 | dccp_ackvec_exit(); |
1092 | dccp_sysctl_exit(); | ||
924 | } | 1093 | } |
925 | 1094 | ||
926 | module_init(dccp_init); | 1095 | module_init(dccp_init); |
927 | module_exit(dccp_fini); | 1096 | module_exit(dccp_fini); |
928 | 1097 | ||
929 | /* | ||
930 | * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) | ||
931 | * values directly, Also cover the case where the protocol is not specified, | ||
932 | * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP | ||
933 | */ | ||
934 | MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); | ||
935 | MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); | ||
936 | MODULE_LICENSE("GPL"); | 1098 | MODULE_LICENSE("GPL"); |
937 | MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); | 1099 | MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>"); |
938 | MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); | 1100 | MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); |