linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / net / dccp / proto.c
index 6f14bb5..65b11ea 100644 (file)
@@ -9,6 +9,7 @@
  *     published by the Free Software Foundation.
  */
 
+#include <linux/config.h>
 #include <linux/dccp.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -22,7 +23,9 @@
 #include <linux/random.h>
 #include <net/checksum.h>
 
+#include <net/inet_common.h>
 #include <net/inet_sock.h>
+#include <net/protocol.h>
 #include <net/sock.h>
 #include <net/xfrm.h>
 
@@ -34,7 +37,6 @@
 
 #include "ccid.h"
 #include "dccp.h"
-#include "feat.h"
 
 DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
 
@@ -44,66 +46,12 @@ atomic_t dccp_orphan_count = ATOMIC_INIT(0);
 
 EXPORT_SYMBOL_GPL(dccp_orphan_count);
 
-struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
-       .lhash_lock     = RW_LOCK_UNLOCKED,
-       .lhash_users    = ATOMIC_INIT(0),
-       .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
+static struct net_protocol dccp_protocol = {
+       .handler        = dccp_v4_rcv,
+       .err_handler    = dccp_v4_err,
+       .no_policy      = 1,
 };
 
-EXPORT_SYMBOL_GPL(dccp_hashinfo);
-
-void dccp_set_state(struct sock *sk, const int state)
-{
-       const int oldstate = sk->sk_state;
-
-       dccp_pr_debug("%s(%p) %-10.10s -> %s\n",
-                     dccp_role(sk), sk,
-                     dccp_state_name(oldstate), dccp_state_name(state));
-       WARN_ON(state == oldstate);
-
-       switch (state) {
-       case DCCP_OPEN:
-               if (oldstate != DCCP_OPEN)
-                       DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
-               break;
-
-       case DCCP_CLOSED:
-               if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN)
-                       DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
-
-               sk->sk_prot->unhash(sk);
-               if (inet_csk(sk)->icsk_bind_hash != NULL &&
-                   !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
-                       inet_put_port(&dccp_hashinfo, sk);
-               /* fall through */
-       default:
-               if (oldstate == DCCP_OPEN)
-                       DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
-       }
-
-       /* Change state AFTER socket is unhashed to avoid closed
-        * socket sitting in hash tables.
-        */
-       sk->sk_state = state;
-}
-
-EXPORT_SYMBOL_GPL(dccp_set_state);
-
-void dccp_done(struct sock *sk)
-{
-       dccp_set_state(sk, DCCP_CLOSED);
-       dccp_clear_xmit_timers(sk);
-
-       sk->sk_shutdown = SHUTDOWN_MASK;
-
-       if (!sock_flag(sk, SOCK_DEAD))
-               sk->sk_state_change(sk);
-       else
-               inet_csk_destroy_sock(sk);
-}
-
-EXPORT_SYMBOL_GPL(dccp_done);
-
 const char *dccp_packet_name(const int type)
 {
        static const char *dccp_packet_names[] = {
@@ -148,120 +96,6 @@ const char *dccp_state_name(const int state)
 
 EXPORT_SYMBOL_GPL(dccp_state_name);
 
-void dccp_hash(struct sock *sk)
-{
-       inet_hash(&dccp_hashinfo, sk);
-}
-
-EXPORT_SYMBOL_GPL(dccp_hash);
-
-void dccp_unhash(struct sock *sk)
-{
-       inet_unhash(&dccp_hashinfo, sk);
-}
-
-EXPORT_SYMBOL_GPL(dccp_unhash);
-
-int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
-{
-       struct dccp_sock *dp = dccp_sk(sk);
-       struct dccp_minisock *dmsk = dccp_msk(sk);
-       struct inet_connection_sock *icsk = inet_csk(sk);
-
-       dccp_minisock_init(&dp->dccps_minisock);
-       do_gettimeofday(&dp->dccps_epoch);
-
-       /*
-        * FIXME: We're hardcoding the CCID, and doing this at this point makes
-        * the listening (master) sock get CCID control blocks, which is not
-        * necessary, but for now, to not mess with the test userspace apps,
-        * lets leave it here, later the real solution is to do this in a
-        * setsockopt(CCIDs-I-want/accept). -acme
-        */
-       if (likely(ctl_sock_initialized)) {
-               int rc = dccp_feat_init(dmsk);
-
-               if (rc)
-                       return rc;
-
-               if (dmsk->dccpms_send_ack_vector) {
-                       dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
-                       if (dp->dccps_hc_rx_ackvec == NULL)
-                               return -ENOMEM;
-               }
-               dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
-                                                     sk, GFP_KERNEL);
-               dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
-                                                     sk, GFP_KERNEL);
-               if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
-                            dp->dccps_hc_tx_ccid == NULL)) {
-                       ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
-                       ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
-                       if (dmsk->dccpms_send_ack_vector) {
-                               dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
-                               dp->dccps_hc_rx_ackvec = NULL;
-                       }
-                       dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
-                       return -ENOMEM;
-               }
-       } else {
-               /* control socket doesn't need feat nego */
-               INIT_LIST_HEAD(&dmsk->dccpms_pending);
-               INIT_LIST_HEAD(&dmsk->dccpms_conf);
-       }
-
-       dccp_init_xmit_timers(sk);
-       icsk->icsk_rto          = DCCP_TIMEOUT_INIT;
-       sk->sk_state            = DCCP_CLOSED;
-       sk->sk_write_space      = dccp_write_space;
-       icsk->icsk_sync_mss     = dccp_sync_mss;
-       dp->dccps_mss_cache     = 536;
-       dp->dccps_role          = DCCP_ROLE_UNDEFINED;
-       dp->dccps_service       = DCCP_SERVICE_INVALID_VALUE;
-       dp->dccps_l_ack_ratio   = dp->dccps_r_ack_ratio = 1;
-
-       return 0;
-}
-
-EXPORT_SYMBOL_GPL(dccp_init_sock);
-
-int dccp_destroy_sock(struct sock *sk)
-{
-       struct dccp_sock *dp = dccp_sk(sk);
-       struct dccp_minisock *dmsk = dccp_msk(sk);
-
-       /*
-        * DCCP doesn't use sk_write_queue, just sk_send_head
-        * for retransmissions
-        */
-       if (sk->sk_send_head != NULL) {
-               kfree_skb(sk->sk_send_head);
-               sk->sk_send_head = NULL;
-       }
-
-       /* Clean up a referenced DCCP bind bucket. */
-       if (inet_csk(sk)->icsk_bind_hash != NULL)
-               inet_put_port(&dccp_hashinfo, sk);
-
-       kfree(dp->dccps_service_list);
-       dp->dccps_service_list = NULL;
-
-       if (dmsk->dccpms_send_ack_vector) {
-               dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
-               dp->dccps_hc_rx_ackvec = NULL;
-       }
-       ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
-       ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
-       dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
-
-       /* clean up feature negotiation state */
-       dccp_feat_clean(dmsk);
-
-       return 0;
-}
-
-EXPORT_SYMBOL_GPL(dccp_destroy_sock);
-
 static inline int dccp_listen_start(struct sock *sk)
 {
        struct dccp_sock *dp = dccp_sk(sk);
@@ -349,7 +183,7 @@ unsigned int dccp_poll(struct file *file, struct socket *sock,
        if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == DCCP_CLOSED)
                mask |= POLLHUP;
        if (sk->sk_shutdown & RCV_SHUTDOWN)
-               mask |= POLLIN | POLLRDNORM | POLLRDHUP;
+               mask |= POLLIN | POLLRDNORM;
 
        /* Connected? */
        if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_RESPOND)) {
@@ -386,7 +220,7 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 
 EXPORT_SYMBOL_GPL(dccp_ioctl);
 
-static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
+static int dccp_setsockopt_service(struct sock *sk, const u32 service,
                                   char __user *optval, int optlen)
 {
        struct dccp_sock *dp = dccp_sk(sk);
@@ -421,46 +255,18 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
        return 0;
 }
 
-/* byte 1 is feature.  the rest is the preference list */
-static int dccp_setsockopt_change(struct sock *sk, int type,
-                                 struct dccp_so_feat __user *optval)
-{
-       struct dccp_so_feat opt;
-       u8 *val;
-       int rc;
-
-       if (copy_from_user(&opt, optval, sizeof(opt)))
-               return -EFAULT;
-
-       val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
-       if (!val)
-               return -ENOMEM;
-
-       if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
-               rc = -EFAULT;
-               goto out_free_val;
-       }
-
-       rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
-                             val, opt.dccpsf_len, GFP_KERNEL);
-       if (rc)
-               goto out_free_val;
-
-out:
-       return rc;
-
-out_free_val:
-       kfree(val);
-       goto out;
-}
-
-static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
-               char __user *optval, int optlen)
+int dccp_setsockopt(struct sock *sk, int level, int optname,
+                   char __user *optval, int optlen)
 {
        struct dccp_sock *dp;
        int err;
        int val;
 
+       if (level != SOL_DCCP)
+               return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
+                                                            optname, optval,
+                                                            optlen);
+
        if (optlen < sizeof(int))
                return -EINVAL;
 
@@ -478,25 +284,6 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
        case DCCP_SOCKOPT_PACKET_SIZE:
                dp->dccps_packet_size = val;
                break;
-
-       case DCCP_SOCKOPT_CHANGE_L:
-               if (optlen != sizeof(struct dccp_so_feat))
-                       err = -EINVAL;
-               else
-                       err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
-                                                    (struct dccp_so_feat __user *)
-                                                    optval);
-               break;
-
-       case DCCP_SOCKOPT_CHANGE_R:
-               if (optlen != sizeof(struct dccp_so_feat))
-                       err = -EINVAL;
-               else
-                       err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
-                                                    (struct dccp_so_feat __user *)
-                                                    optval);
-               break;
-
        default:
                err = -ENOPROTOOPT;
                break;
@@ -506,33 +293,10 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
-int dccp_setsockopt(struct sock *sk, int level, int optname,
-                   char __user *optval, int optlen)
-{
-       if (level != SOL_DCCP)
-               return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
-                                                            optname, optval,
-                                                            optlen);
-       return do_dccp_setsockopt(sk, level, optname, optval, optlen);
-}
-
 EXPORT_SYMBOL_GPL(dccp_setsockopt);
 
-#ifdef CONFIG_COMPAT
-int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
-                          char __user *optval, int optlen)
-{
-       if (level != SOL_DCCP)
-               return inet_csk_compat_setsockopt(sk, level, optname,
-                                                 optval, optlen);
-       return do_dccp_setsockopt(sk, level, optname, optval, optlen);
-}
-
-EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
-#endif
-
 static int dccp_getsockopt_service(struct sock *sk, int len,
-                                  __be32 __user *optval,
+                                  u32 __user *optval,
                                   int __user *optlen)
 {
        const struct dccp_sock *dp = dccp_sk(sk);
@@ -562,12 +326,16 @@ out:
        return err;
 }
 
-static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
+int dccp_getsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
        struct dccp_sock *dp;
        int val, len;
 
+       if (level != SOL_DCCP)
+               return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
+                                                            optname, optval,
+                                                            optlen);
        if (get_user(len, optlen))
                return -EFAULT;
 
@@ -583,7 +351,7 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
                break;
        case DCCP_SOCKOPT_SERVICE:
                return dccp_getsockopt_service(sk, len,
-                                              (__be32 __user *)optval, optlen);
+                                              (u32 __user *)optval, optlen);
        case 128 ... 191:
                return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
                                             len, (u32 __user *)optval, optlen);
@@ -600,31 +368,8 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
        return 0;
 }
 
-int dccp_getsockopt(struct sock *sk, int level, int optname,
-                   char __user *optval, int __user *optlen)
-{
-       if (level != SOL_DCCP)
-               return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
-                                                            optname, optval,
-                                                            optlen);
-       return do_dccp_getsockopt(sk, level, optname, optval, optlen);
-}
-
 EXPORT_SYMBOL_GPL(dccp_getsockopt);
 
-#ifdef CONFIG_COMPAT
-int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
-                          char __user *optval, int __user *optlen)
-{
-       if (level != SOL_DCCP)
-               return inet_csk_compat_getsockopt(sk, level, optname,
-                                                 optval, optlen);
-       return do_dccp_getsockopt(sk, level, optname, optval, optlen);
-}
-
-EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
-#endif
-
 int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                 size_t len)
 {
@@ -718,7 +463,7 @@ int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                }
                dccp_pr_debug("packet_type=%s\n",
                              dccp_packet_name(dh->dccph_type));
-               sk_eat_skb(sk, skb, 0);
+               sk_eat_skb(sk, skb);
 verify_sock_status:
                if (sock_flag(sk, SOCK_DONE)) {
                        len = 0;
@@ -772,7 +517,7 @@ verify_sock_status:
                }
        found_fin_ok:
                if (!(flags & MSG_PEEK))
-                       sk_eat_skb(sk, skb, 0);
+                       sk_eat_skb(sk, skb);
                break;
        } while (1);
 out:
@@ -847,7 +592,6 @@ static int dccp_close_state(struct sock *sk)
 void dccp_close(struct sock *sk, long timeout)
 {
        struct sk_buff *skb;
-       int state;
 
        lock_sock(sk);
 
@@ -882,11 +626,6 @@ void dccp_close(struct sock *sk, long timeout)
        sk_stream_wait_close(sk, timeout);
 
 adjudge_to_death:
-       state = sk->sk_state;
-       sock_hold(sk);
-       sock_orphan(sk);
-       atomic_inc(sk->sk_prot->orphan_count);
-
        /*
         * It is the last release_sock in its life. It will remove backlog.
         */
@@ -899,9 +638,8 @@ adjudge_to_death:
        bh_lock_sock(sk);
        BUG_TRAP(!sock_owned_by_user(sk));
 
-       /* Have we already been destroyed by a softirq or backlog? */
-       if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
-               goto out;
+       sock_hold(sk);
+       sock_orphan(sk);
 
        /*
         * The last release_sock may have processed the CLOSE or RESET
@@ -921,12 +659,12 @@ adjudge_to_death:
 #endif
        }
 
+       atomic_inc(sk->sk_prot->orphan_count);
        if (sk->sk_state == DCCP_CLOSED)
                inet_csk_destroy_sock(sk);
 
        /* Otherwise, socket is reprieved until protocol close. */
 
-out:
        bh_unlock_sock(sk);
        local_bh_enable();
        sock_put(sk);
@@ -941,7 +679,84 @@ void dccp_shutdown(struct sock *sk, int how)
 
 EXPORT_SYMBOL_GPL(dccp_shutdown);
 
-static int __init dccp_mib_init(void)
+static const struct proto_ops inet_dccp_ops = {
+       .family         = PF_INET,
+       .owner          = THIS_MODULE,
+       .release        = inet_release,
+       .bind           = inet_bind,
+       .connect        = inet_stream_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = inet_accept,
+       .getname        = inet_getname,
+       /* FIXME: work on tcp_poll to rename it to inet_csk_poll */
+       .poll           = dccp_poll,
+       .ioctl          = inet_ioctl,
+       /* FIXME: work on inet_listen to rename it to sock_common_listen */
+       .listen         = inet_dccp_listen,
+       .shutdown       = inet_shutdown,
+       .setsockopt     = sock_common_setsockopt,
+       .getsockopt     = sock_common_getsockopt,
+       .sendmsg        = inet_sendmsg,
+       .recvmsg        = sock_common_recvmsg,
+       .mmap           = sock_no_mmap,
+       .sendpage       = sock_no_sendpage,
+};
+
+extern struct net_proto_family inet_family_ops;
+
+static struct inet_protosw dccp_v4_protosw = {
+       .type           = SOCK_DCCP,
+       .protocol       = IPPROTO_DCCP,
+       .prot           = &dccp_prot,
+       .ops            = &inet_dccp_ops,
+       .capability     = -1,
+       .no_check       = 0,
+       .flags          = INET_PROTOSW_ICSK,
+};
+
+/*
+ * This is the global socket data structure used for responding to
+ * the Out-of-the-blue (OOTB) packets. A control sock will be created
+ * for this socket at the initialization time.
+ */
+struct socket *dccp_ctl_socket;
+
+static char dccp_ctl_socket_err_msg[] __initdata =
+       KERN_ERR "DCCP: Failed to create the control socket.\n";
+
+static int __init dccp_ctl_sock_init(void)
+{
+       int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP,
+                                 &dccp_ctl_socket);
+       if (rc < 0)
+               printk(dccp_ctl_socket_err_msg);
+       else {
+               dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
+               inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1;
+
+               /* Unhash it so that IP input processing does not even
+                * see it, we do not wish this socket to see incoming
+                * packets.
+                */
+               dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk);
+       }
+
+       return rc;
+}
+
+#ifdef CONFIG_IP_DCCP_UNLOAD_HACK
+void dccp_ctl_sock_exit(void)
+{
+       if (dccp_ctl_socket != NULL) {
+               sock_release(dccp_ctl_socket);
+               dccp_ctl_socket = NULL;
+       }
+}
+
+EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit);
+#endif
+
+static int __init init_dccp_v4_mibs(void)
 {
        int rc = -ENOMEM;
 
@@ -963,13 +778,6 @@ out_free_one:
 
 }
 
-static void dccp_mib_exit(void)
-{
-       free_percpu(dccp_statistics[0]);
-       free_percpu(dccp_statistics[1]);
-       dccp_statistics[0] = dccp_statistics[1] = NULL;
-}
-
 static int thash_entries;
 module_param(thash_entries, int, 0444);
 MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
@@ -986,14 +794,17 @@ static int __init dccp_init(void)
 {
        unsigned long goal;
        int ehash_order, bhash_order, i;
-       int rc = -ENOBUFS;
+       int rc = proto_register(&dccp_prot, 1);
+
+       if (rc)
+               goto out;
 
        dccp_hashinfo.bind_bucket_cachep =
                kmem_cache_create("dccp_bind_bucket",
                                  sizeof(struct inet_bind_bucket), 0,
                                  SLAB_HWCACHE_ALIGN, NULL, NULL);
        if (!dccp_hashinfo.bind_bucket_cachep)
-               goto out;
+               goto out_proto_unregister;
 
        /*
         * Size and allocate the main established and bind bucket
@@ -1055,23 +866,27 @@ static int __init dccp_init(void)
                INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
        }
 
-       rc = dccp_mib_init();
-       if (rc)
+       if (init_dccp_v4_mibs())
                goto out_free_dccp_bhash;
 
-       rc = dccp_ackvec_init();
-       if (rc)
-               goto out_free_dccp_mib;
+       rc = -EAGAIN;
+       if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP))
+               goto out_free_dccp_v4_mibs;
 
-       rc = dccp_sysctl_init();
+       inet_register_protosw(&dccp_v4_protosw);
+
+       rc = dccp_ctl_sock_init();
        if (rc)
-               goto out_ackvec_exit;
+               goto out_unregister_protosw;
 out:
        return rc;
-out_ackvec_exit:
-       dccp_ackvec_exit();
-out_free_dccp_mib:
-       dccp_mib_exit();
+out_unregister_protosw:
+       inet_unregister_protosw(&dccp_v4_protosw);
+       inet_del_protocol(&dccp_protocol, IPPROTO_DCCP);
+out_free_dccp_v4_mibs:
+       free_percpu(dccp_statistics[0]);
+       free_percpu(dccp_statistics[1]);
+       dccp_statistics[0] = dccp_statistics[1] = NULL;
 out_free_dccp_bhash:
        free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
        dccp_hashinfo.bhash = NULL;
@@ -1081,12 +896,23 @@ out_free_dccp_ehash:
 out_free_bind_bucket_cachep:
        kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
        dccp_hashinfo.bind_bucket_cachep = NULL;
+out_proto_unregister:
+       proto_unregister(&dccp_prot);
        goto out;
 }
 
+static const char dccp_del_proto_err_msg[] __exitdata =
+       KERN_ERR "can't remove dccp net_protocol\n";
+
 static void __exit dccp_fini(void)
 {
-       dccp_mib_exit();
+       inet_unregister_protosw(&dccp_v4_protosw);
+
+       if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
+               printk(dccp_del_proto_err_msg);
+
+       free_percpu(dccp_statistics[0]);
+       free_percpu(dccp_statistics[1]);
        free_pages((unsigned long)dccp_hashinfo.bhash,
                   get_order(dccp_hashinfo.bhash_size *
                             sizeof(struct inet_bind_hashbucket)));
@@ -1094,13 +920,19 @@ static void __exit dccp_fini(void)
                   get_order(dccp_hashinfo.ehash_size *
                             sizeof(struct inet_ehash_bucket)));
        kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
-       dccp_ackvec_exit();
-       dccp_sysctl_exit();
+       proto_unregister(&dccp_prot);
 }
 
 module_init(dccp_init);
 module_exit(dccp_fini);
 
+/*
+ * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
+ * values directly, Also cover the case where the protocol is not specified,
+ * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP
+ */
+MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6");
+MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
 MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");