fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / appletalk / ddp.c
index 4185d7b..7c69506 100644 (file)
@@ -9,6 +9,7 @@
  *             Wesley Craig <netatalk@umich.edu>
  *
  *     Fixes:
+ *             Neil Horman             :       Added missing device ioctls
  *             Michael Callahan        :       Made routing work
  *             Wesley Craig            :       Fix probing to listen to a
  *                                             passed node id.
  * 
  */
 
-#include <linux/config.h>
+#include <linux/capability.h>
 #include <linux/module.h>
-#include <linux/tcp.h>
 #include <linux/if_arp.h>
 #include <linux/termios.h>     /* For TIOCOUTQ/INQ */
 #include <net/datalink.h>
 #include <net/psnap.h>
 #include <net/sock.h>
+#include <net/tcp_states.h>
 #include <net/route.h>
 #include <linux/atalk.h>
-
-extern void aarp_cleanup_module(void);
-
-extern void aarp_probe_network(struct atalk_iface *atif);
-extern int  aarp_proxy_probe_network(struct atalk_iface *atif,
-                                    struct atalk_addr *sa);
-extern void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa);
-
-extern void atalk_register_sysctl(void);
-extern void atalk_unregister_sysctl(void);
+#include "../core/kmap_skb.h"
 
 struct datalink_proto *ddp_dl, *aarp_dl;
-static struct proto_ops atalk_dgram_ops;
+static const struct proto_ops atalk_dgram_ops;
 
 /**************************************************************************\
 *                                                                          *
@@ -81,20 +73,13 @@ static struct proto_ops atalk_dgram_ops;
 \**************************************************************************/
 
 HLIST_HEAD(atalk_sockets);
-rwlock_t atalk_sockets_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_sockets_lock);
 
 static inline void __atalk_insert_socket(struct sock *sk)
 {
        sk_add_node(sk, &atalk_sockets);
 }
 
-static inline void atalk_insert_socket(struct sock *sk)
-{
-       write_lock_bh(&atalk_sockets_lock);
-       __atalk_insert_socket(sk);
-       write_unlock_bh(&atalk_sockets_lock);
-}
-
 static inline void atalk_remove_socket(struct sock *sk)
 {
        write_lock_bh(&atalk_sockets_lock);
@@ -116,8 +101,7 @@ static struct sock *atalk_search_socket(struct sockaddr_at *to,
                        continue;
 
                if (to->sat_addr.s_net == ATADDR_ANYNET &&
-                   to->sat_addr.s_node == ATADDR_BCAST &&
-                   at->src_net == atif->address.s_net)
+                   to->sat_addr.s_node == ATADDR_BCAST)
                        goto found;
 
                if (to->sat_addr.s_net == at->src_net &&
@@ -210,10 +194,10 @@ static inline void atalk_destroy_socket(struct sock *sk)
 
 /* Anti-deadlock ordering is atalk_routes_lock --> iface_lock -DaveM */
 struct atalk_route *atalk_routes;
-rwlock_t atalk_routes_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_routes_lock);
 
 struct atalk_iface *atalk_interfaces;
-rwlock_t atalk_interfaces_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(atalk_interfaces_lock);
 
 /* For probing devices or in a routerless network */
 struct atalk_route atrtr_default;
@@ -244,12 +228,11 @@ static void atif_drop_device(struct net_device *dev)
 static struct atalk_iface *atif_add_device(struct net_device *dev,
                                           struct atalk_addr *sa)
 {
-       struct atalk_iface *iface = kmalloc(sizeof(*iface), GFP_KERNEL);
+       struct atalk_iface *iface = kzalloc(sizeof(*iface), GFP_KERNEL);
 
        if (!iface)
                goto out;
 
-       memset(iface, 0, sizeof(*iface));
        dev_hold(dev);
        iface->dev = dev;
        dev->atalk_ptr = iface;
@@ -417,7 +400,7 @@ out_err:
 }
 
 /* Find a match for a specific network:node pair */
-static struct atalk_iface *atalk_find_interface(int net, int node)
+static struct atalk_iface *atalk_find_interface(__be16 net, int node)
 {
        struct atalk_iface *iface;
 
@@ -576,12 +559,11 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint)
        }
 
        if (!rt) {
-               rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
+               rt = kzalloc(sizeof(*rt), GFP_ATOMIC);
 
                retval = -ENOBUFS;
                if (!rt)
-                       goto out;
-               memset(rt, 0, sizeof(*rt));
+                       goto out_unlock;
 
                rt->next = atalk_routes;
                atalk_routes = rt;
@@ -589,6 +571,7 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint)
 
        /* Fill in the routing entry */
        rt->target  = ta->sat_addr;
+       dev_hold(devhint);
        rt->dev     = devhint;
        rt->flags   = r->rt_flags;
        rt->gateway = ga->sat_addr;
@@ -629,7 +612,7 @@ out:
  * Called when a device is downed. Just throw away any routes
  * via it.
  */
-void atrtr_device_down(struct net_device *dev)
+static void atrtr_device_down(struct net_device *dev)
 {
        struct atalk_route **r = &atalk_routes;
        struct atalk_route *tmp;
@@ -1020,7 +1003,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
        return sum;
 }
 
-static unsigned short atalk_checksum(const struct sk_buff *skb, int len)
+static __be16 atalk_checksum(const struct sk_buff *skb, int len)
 {
        unsigned long sum;
 
@@ -1028,9 +1011,15 @@ static unsigned short atalk_checksum(const struct sk_buff *skb, int len)
        sum = atalk_sum_skb(skb, 4, len-4, 0);
 
        /* Use 0xFFFF for 0. 0 itself means none */
-       return sum ? htons((unsigned short)sum) : 0xFFFF;
+       return sum ? htons((unsigned short)sum) : htons(0xFFFF);
 }
 
+static struct proto ddp_proto = {
+       .name     = "DDP",
+       .owner    = THIS_MODULE,
+       .obj_size = sizeof(struct atalk_sock),
+};
+
 /*
  * Create a socket. Initialise the socket, blank the addresses
  * set the state.
@@ -1038,7 +1027,6 @@ static unsigned short atalk_checksum(const struct sk_buff *skb, int len)
 static int atalk_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
-       struct atalk_sock *at;
        int rc = -ESOCKTNOSUPPORT;
 
        /*
@@ -1048,25 +1036,17 @@ static int atalk_create(struct socket *sock, int protocol)
        if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
                goto out;
        rc = -ENOMEM;
-       sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, 1, NULL);
+       sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, &ddp_proto, 1);
        if (!sk)
                goto out;
-       at = sk->sk_protinfo = kmalloc(sizeof(*at), GFP_KERNEL);
-       if (!at)
-               goto outsk;
-       memset(at, 0, sizeof(*at));
        rc = 0;
        sock->ops = &atalk_dgram_ops;
        sock_init_data(sock, sk);
-       sk_set_owner(sk, THIS_MODULE);
 
        /* Checksums on by default */
-       sk->sk_zapped = 1;
+       sock_set_flag(sk, SOCK_ZAPPED);
 out:
        return rc;
-outsk:
-       sk_free(sk);
-       goto out;
 }
 
 /* Free a socket. No work needed */
@@ -1143,7 +1123,7 @@ static int atalk_autobind(struct sock *sk)
 
        n = atalk_pick_and_bind_port(sk, &sat);
        if (!n)
-               sk->sk_zapped = 0;
+               sock_reset_flag(sk, SOCK_ZAPPED);
 out:
        return n;
 }
@@ -1155,7 +1135,8 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        struct sock *sk = sock->sk;
        struct atalk_sock *at = at_sk(sk);
 
-       if (!sk->sk_zapped || addr_len != sizeof(struct sockaddr_at))
+       if (!sock_flag(sk, SOCK_ZAPPED) ||
+           addr_len != sizeof(struct sockaddr_at))
                return -EINVAL;
 
        if (addr->sat_family != AF_APPLETALK)
@@ -1190,7 +1171,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                        return -EADDRINUSE;
        }
 
-       sk->sk_zapped = 0;
+       sock_reset_flag(sk, SOCK_ZAPPED);
        return 0;
 }
 
@@ -1225,7 +1206,7 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
 #endif                 
        }
 
-       if (sk->sk_zapped)
+       if (sock_flag(sk, SOCK_ZAPPED))
                if (atalk_autobind(sk) < 0)
                        return -EBUSY;
 
@@ -1252,7 +1233,7 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
        struct sock *sk = sock->sk;
        struct atalk_sock *at = at_sk(sk);
 
-       if (sk->sk_zapped)
+       if (sock_flag(sk, SOCK_ZAPPED))
                if (atalk_autobind(sk) < 0)
                        return -ENOBUFS;
 
@@ -1309,7 +1290,7 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
 #endif
 
 static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
-                              struct ddpehdr *ddp, struct ddpebits *ddphv,
+                              struct ddpehdr *ddp, __u16 len_hops,
                               int origlen)
 {
        struct atalk_route *rt;
@@ -1337,10 +1318,12 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
 
        /* Route the packet */
        rt = atrtr_find(&ta);
-       if (!rt || ddphv->deh_hops == DDP_MAXHOPS)
+       /* increment hops count */
+       len_hops += 1 << 10;
+       if (!rt || !(len_hops & (15 << 10)))
                goto free_it;
+
        /* FIXME: use skb->cb to be able to use shared skbs */
-       ddphv->deh_hops++;
 
        /*
         * Route goes through another gateway, so set the target to the
@@ -1355,11 +1338,10 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
         /* Fix up skb->len field */
         skb_trim(skb, min_t(unsigned int, origlen,
                            (rt->dev->hard_header_len +
-                            ddp_dl->header_length + ddphv->deh_len)));
+                            ddp_dl->header_length + (len_hops & 1023))));
 
-       /* Mend the byte order */
        /* FIXME: use skb->cb to be able to use shared skbs */
-       *((__u16 *)ddp) = ntohs(*((__u16 *)ddphv));
+       ddp->deh_len_hops = htons(len_hops);
 
        /*
         * Send the buffer onwards
@@ -1407,14 +1389,14 @@ free_it:
  *     [ie ARPHRD_ETHERTALK]
  */
 static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
-                    struct packet_type *pt)
+                    struct packet_type *pt, struct net_device *orig_dev)
 {
        struct ddpehdr *ddp;
        struct sock *sock;
        struct atalk_iface *atif;
        struct sockaddr_at tosat;
         int origlen;
-        struct ddpebits ddphv;
+       __u16 len_hops;
 
        /* Don't mangle buffer if shared */
        if (!(skb = skb_share_check(skb, GFP_ATOMIC))) 
@@ -1426,31 +1408,29 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
 
        ddp = ddp_hdr(skb);
 
-       /*
-        *      Fix up the length field [Ok this is horrible but otherwise
-        *      I end up with unions of bit fields and messy bit field order
-        *      compiler/endian dependencies..]
-        */
-       *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp));
+       len_hops = ntohs(ddp->deh_len_hops);
 
        /* Trim buffer in case of stray trailing data */
        origlen = skb->len;
-       skb_trim(skb, min_t(unsigned int, skb->len, ddphv.deh_len));
+       skb_trim(skb, min_t(unsigned int, skb->len, len_hops & 1023));
 
        /*
         * Size check to see if ddp->deh_len was crap
         * (Otherwise we'll detonate most spectacularly
-        * in the middle of recvmsg()).
+        * in the middle of atalk_checksum() or recvmsg()).
         */
-       if (skb->len < sizeof(*ddp))
+       if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) {
+               pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, "
+                        "skb->len=%u)\n", len_hops & 1023, skb->len);
                goto freeit;
+       }
 
        /*
         * Any checksums. Note we don't do htons() on this == is assumed to be
         * valid for net byte orders all over the networking code...
         */
        if (ddp->deh_sum &&
-           atalk_checksum(skb, ddphv.deh_len) != ddp->deh_sum)
+           atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum)
                /* Not a valid AppleTalk frame - dustbin time */
                goto freeit;
 
@@ -1460,9 +1440,11 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
        else
                atif = atalk_find_interface(ddp->deh_dnet, ddp->deh_dnode);
 
-       /* Not ours, so we route the packet via the correct AppleTalk iface */
        if (!atif) {
-               atalk_route_packet(skb, dev, ddp, &ddphv, origlen);
+               /* Not ours, so we route the packet via the correct
+                * AppleTalk iface
+                */
+               atalk_route_packet(skb, dev, ddp, len_hops, origlen);
                goto out;
        }
 
@@ -1499,7 +1481,7 @@ freeit:
  * header and append a long one.
  */
 static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
-                       struct packet_type *pt)
+                    struct packet_type *pt, struct net_device *orig_dev)
 {
        /* Expand any short form frames */
        if (skb->mac.raw[2] == 1) {
@@ -1507,7 +1489,7 @@ static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
                /* Find our address */
                struct atalk_addr *ap = atalk_find_dev_addr(dev);
 
-               if (!ap || skb->len < sizeof(struct ddpshdr))
+               if (!ap || skb->len < sizeof(__be16) || skb->len > 1023)
                        goto freeit;
 
                /* Don't mangle buffer if shared */
@@ -1537,15 +1519,12 @@ static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
                /*
                 * Not sure about this bit...
                 */
-               ddp->deh_len   = skb->len;
-               ddp->deh_hops  = DDP_MAXHOPS;   /* Non routable, so force a drop
-                                                  if we slip up later */
-               /* Mend the byte order */
-               *((__u16 *)ddp) = htons(*((__u16 *)ddp));
+               /* Non routable, so force a drop if we slip up later */
+               ddp->deh_len_hops = htons(skb->len + (DDP_MAXHOPS << 10));
        }
        skb->h.raw = skb->data;
 
-       return atalk_rcv(skb, dev, pt);
+       return atalk_rcv(skb, dev, pt, orig_dev);
 freeit:
        kfree_skb(skb);
        return 0;
@@ -1574,7 +1553,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                return -EMSGSIZE;
 
        if (usat) {
-               if (sk->sk_zapped)
+               if (sock_flag(sk, SOCK_ZAPPED))
                        if (atalk_autobind(sk) < 0)
                                return -EBUSY;
 
@@ -1609,10 +1588,6 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
 
        if (usat->sat_addr.s_net || usat->sat_addr.s_node == ATADDR_ANYNODE) {
                rt = atrtr_find(&usat->sat_addr);
-               if (!rt)
-                       return -ENETUNREACH;
-
-               dev = rt->dev;
        } else {
                struct atalk_addr at_hint;
 
@@ -1620,11 +1595,11 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                at_hint.s_net  = at->src_net;
 
                rt = atrtr_find(&at_hint);
-               if (!rt)
-                       return -ENETUNREACH;
-
-               dev = rt->dev;
        }
+       if (!rt)
+               return -ENETUNREACH;
+
+       dev = rt->dev;
 
        SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n",
                        sk, size, dev->name);
@@ -1642,16 +1617,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
 
        ddp = (struct ddpehdr *)skb_put(skb, sizeof(struct ddpehdr));
-       ddp->deh_pad  = 0;
-       ddp->deh_hops = 0;
-       ddp->deh_len  = len + sizeof(*ddp);
-       /*
-        * Fix up the length field [Ok this is horrible but otherwise
-        * I end up with unions of bit fields and messy bit field order
-        * compiler/endian dependencies..
-        */
-       *((__u16 *)ddp) = ntohs(*((__u16 *)ddp));
-
+       ddp->deh_len_hops  = htons(len + sizeof(*ddp));
        ddp->deh_dnet  = usat->sat_addr.s_net;
        ddp->deh_snet  = at->src_net;
        ddp->deh_dnode = usat->sat_addr.s_node;
@@ -1694,6 +1660,20 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk);
                /* loop back */
                skb_orphan(skb);
+               if (ddp->deh_dnode == ATADDR_BCAST) {
+                       struct atalk_addr at_lo;
+
+                       at_lo.s_node = 0;
+                       at_lo.s_net  = 0;
+
+                       rt = atrtr_find(&at_lo);
+                       if (!rt) {
+                               kfree_skb(skb);
+                               return -ENETUNREACH;
+                       }
+                       dev = rt->dev;
+                       skb->dev = dev;
+               }
                ddp_dl->request(ddp_dl, skb, dev->dev_addr);
        } else {
                SOCK_DEBUG(sk, "SK %p: send out.\n", sk);
@@ -1718,8 +1698,8 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        struct sockaddr_at *sat = (struct sockaddr_at *)msg->msg_name;
        struct ddpehdr *ddp;
        int copied = 0;
+       int offset = 0;
        int err = 0;
-        struct ddpebits ddphv;
        struct sk_buff *skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                                flags & MSG_DONTWAIT, &err);
        if (!skb)
@@ -1727,25 +1707,18 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
 
        /* FIXME: use skb->cb to be able to use shared skbs */
        ddp = ddp_hdr(skb);
-       *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp));
+       copied = ntohs(ddp->deh_len_hops) & 1023;
 
-       if (sk->sk_type == SOCK_RAW) {
-               copied = ddphv.deh_len;
-               if (copied > size) {
-                       copied = size;
-                       msg->msg_flags |= MSG_TRUNC;
-               }
+       if (sk->sk_type != SOCK_RAW) {
+               offset = sizeof(*ddp);
+               copied -= offset;
+       }
 
-               err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-       } else {
-               copied = ddphv.deh_len - sizeof(*ddp);
-               if (copied > size) {
-                       copied = size;
-                       msg->msg_flags |= MSG_TRUNC;
-               }
-               err = skb_copy_datagram_iovec(skb, sizeof(*ddp),
-                                             msg->msg_iov, copied);
+       if (copied > size) {
+               copied = size;
+               msg->msg_flags |= MSG_TRUNC;
        }
+       err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied);
 
        if (!err) {
                if (sat) {
@@ -1767,7 +1740,7 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
  */
 static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-       int rc = -EINVAL;
+       int rc = -ENOIOCTLCMD;
        struct sock *sk = sock->sk;
        void __user *argp = (void __user *)arg;
 
@@ -1817,33 +1790,34 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        rc = atif_ioctl(cmd, argp);
                        rtnl_unlock();
                        break;
-               /* Physical layer ioctl calls */
-               case SIOCSIFLINK:
-               case SIOCGIFHWADDR:
-               case SIOCSIFHWADDR:
-               case SIOCGIFFLAGS:
-               case SIOCSIFFLAGS:
-               case SIOCGIFMTU:
-               case SIOCGIFCONF:
-               case SIOCADDMULTI:
-               case SIOCDELMULTI:
-               case SIOCGIFCOUNT:
-               case SIOCGIFINDEX:
-               case SIOCGIFNAME:
-                       rc = dev_ioctl(cmd, argp);
-                       break;
        }
 
        return rc;
 }
 
+
+#ifdef CONFIG_COMPAT
+static int atalk_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       /*
+        * All Appletalk ioctls except SIOCATALKDIFADDR are standard.  And
+        * SIOCATALKDIFADDR is handled by upper layer as well, so there is
+        * nothing to do.  Eventually SIOCATALKDIFADDR should be moved
+        * here so there is no generic SIOCPROTOPRIVATE translation in the
+        * system.
+        */
+       return -ENOIOCTLCMD;
+}
+#endif
+
+
 static struct net_proto_family atalk_family_ops = {
        .family         = PF_APPLETALK,
        .create         = atalk_create,
        .owner          = THIS_MODULE,
 };
 
-static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
+static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
        .family         = PF_APPLETALK,
        .owner          = THIS_MODULE,
        .release        = atalk_release,
@@ -1854,6 +1828,9 @@ static struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
        .getname        = atalk_getname,
        .poll           = datagram_poll,
        .ioctl          = atalk_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = atalk_compat_ioctl,
+#endif
        .listen         = sock_no_listen,
        .shutdown       = sock_no_shutdown,
        .setsockopt     = sock_no_setsockopt,
@@ -1871,12 +1848,12 @@ static struct notifier_block ddp_notifier = {
        .notifier_call  = ddp_device_event,
 };
 
-struct packet_type ltalk_packet_type = {
+static struct packet_type ltalk_packet_type = {
        .type           = __constant_htons(ETH_P_LOCALTALK),
        .func           = ltalk_rcv,
 };
 
-struct packet_type ppptalk_packet_type = {
+static struct packet_type ppptalk_packet_type = {
        .type           = __constant_htons(ETH_P_PPPTALK),
        .func           = atalk_rcv,
 };
@@ -1894,6 +1871,11 @@ static char atalk_err_snap[] __initdata =
 /* Called by proto.c on kernel start up */
 static int __init atalk_init(void)
 {
+       int rc = proto_register(&ddp_proto, 0);
+
+       if (rc != 0)
+               goto out;
+
        (void)sock_register(&atalk_family_ops);
        ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv);
        if (!ddp_dl)
@@ -1906,7 +1888,8 @@ static int __init atalk_init(void)
        aarp_proto_init();
        atalk_proc_init();
        atalk_register_sysctl();
-       return 0;
+out:
+       return rc;
 }
 module_init(atalk_init);
 
@@ -1931,6 +1914,7 @@ static void __exit atalk_exit(void)
        dev_remove_pack(&ppptalk_packet_type);
        unregister_snap_client(ddp_dl);
        sock_unregister(PF_APPLETALK);
+       proto_unregister(&ddp_proto);
 }
 module_exit(atalk_exit);