net/appletalk: push down BKL into a atalk_dgram_ops
[safe/jmp/linux-2.6] / net / appletalk / ddp.c
index 96dc6bb..31fca64 100644 (file)
@@ -2,7 +2,7 @@
  *     DDP:    An implementation of the AppleTalk DDP protocol for
  *             Ethernet 'ELAP'.
  *
- *             Alan Cox  <Alan.Cox@linux.org>
+ *             Alan Cox  <alan@lxorguk.ukuu.org.uk>
  *
  *             With more than a little assistance from
  *
  *             Bradford Johnson        :       IP-over-DDP (experimental)
  *             Jay Schulist            :       Moved IP-over-DDP to its own
  *                                             driver file. (ipddp.c & ipddp.h)
- *             Jay Schulist            :       Made work as module with 
+ *             Jay Schulist            :       Made work as module with
  *                                             AppleTalk drivers, cleaned it.
  *             Rob Newberry            :       Added proxy AARP and AARP
  *                                             procfs, moved probing to AARP
  *                                             module.
- *              Adrian Sun/ 
- *              Michael Zuelsdorff      :       fix for net.0 packets. don't 
+ *              Adrian Sun/
+ *              Michael Zuelsdorff      :       fix for net.0 packets. don't
  *                                              allow illegal ether/tokentalk
- *                                              port assignment. we lose a 
- *                                              valid localtalk port as a 
+ *                                              port assignment. we lose a
+ *                                              valid localtalk port as a
  *                                              result.
  *             Arnaldo C. de Melo      :       Cleanup, in preparation for
  *                                             shared skb support 8)
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
- * 
+ *
  */
 
 #include <linux/capability.h>
 #include <linux/module.h>
 #include <linux/if_arp.h>
+#include <linux/smp_lock.h>
 #include <linux/termios.h>     /* For TIOCOUTQ/INQ */
 #include <net/datalink.h>
 #include <net/psnap.h>
@@ -61,6 +62,7 @@
 #include <net/tcp_states.h>
 #include <net/route.h>
 #include <linux/atalk.h>
+#include "../core/kmap_skb.h"
 
 struct datalink_proto *ddp_dl, *aarp_dl;
 static const struct proto_ops atalk_dgram_ops;
@@ -99,17 +101,17 @@ static struct sock *atalk_search_socket(struct sockaddr_at *to,
                if (to->sat_port != at->src_port)
                        continue;
 
-               if (to->sat_addr.s_net == ATADDR_ANYNET &&
+               if (to->sat_addr.s_net == ATADDR_ANYNET &&
                    to->sat_addr.s_node == ATADDR_BCAST)
                        goto found;
 
-               if (to->sat_addr.s_net == at->src_net &&
+               if (to->sat_addr.s_net == at->src_net &&
                    (to->sat_addr.s_node == at->src_node ||
                     to->sat_addr.s_node == ATADDR_BCAST ||
                     to->sat_addr.s_node == ATADDR_ANYNODE))
                        goto found;
 
-               /* XXXX.0 -- we got a request for this router. make sure
+               /* XXXX.0 -- we got a request for this router. make sure
                 * that the node is appropriately set. */
                if (to->sat_addr.s_node == ATADDR_ANYNODE &&
                    to->sat_addr.s_net != ATADDR_ANYNET &&
@@ -161,8 +163,7 @@ static void atalk_destroy_timer(unsigned long data)
 {
        struct sock *sk = (struct sock *)data;
 
-       if (atomic_read(&sk->sk_wmem_alloc) ||
-           atomic_read(&sk->sk_rmem_alloc)) {
+       if (sk_has_allocations(sk)) {
                sk->sk_timer.expires = jiffies + SOCK_DESTROY_TIME;
                add_timer(&sk->sk_timer);
        } else
@@ -174,12 +175,10 @@ static inline void atalk_destroy_socket(struct sock *sk)
        atalk_remove_socket(sk);
        skb_queue_purge(&sk->sk_receive_queue);
 
-       if (atomic_read(&sk->sk_wmem_alloc) ||
-           atomic_read(&sk->sk_rmem_alloc)) {
-               init_timer(&sk->sk_timer);
+       if (sk_has_allocations(sk)) {
+               setup_timer(&sk->sk_timer, atalk_destroy_timer,
+                               (unsigned long)sk);
                sk->sk_timer.expires    = jiffies + SOCK_DESTROY_TIME;
-               sk->sk_timer.function   = atalk_destroy_timer;
-               sk->sk_timer.data       = (unsigned long)sk;
                add_timer(&sk->sk_timer);
        } else
                sock_put(sk);
@@ -313,7 +312,7 @@ static int atif_proxy_probe_device(struct atalk_iface *atif,
 
        if (probe_node == ATADDR_ANYNODE)
                probe_node = jiffies & 0xFF;
-               
+
        /* Scan the networks */
        for (netct = 0; netct <= netrange; netct++) {
                /* Sweep the available nodes from a given start */
@@ -416,7 +415,7 @@ static struct atalk_iface *atalk_find_interface(__be16 net, int node)
                if (node == ATADDR_ANYNODE && net != ATADDR_ANYNET &&
                    ntohs(iface->nets.nr_firstnet) <= ntohs(net) &&
                    ntohs(net) <= ntohs(iface->nets.nr_lastnet))
-                       break;
+                       break;
        }
        read_unlock_bh(&atalk_interfaces_lock);
        return iface;
@@ -431,13 +430,13 @@ static struct atalk_iface *atalk_find_interface(__be16 net, int node)
 static struct atalk_route *atrtr_find(struct atalk_addr *target)
 {
        /*
-        * we must search through all routes unless we find a 
+        * we must search through all routes unless we find a
         * host route, because some host routes might overlap
         * network routes
         */
        struct atalk_route *net_route = NULL;
        struct atalk_route *r;
-       
+
        read_lock_bh(&atalk_routes_lock);
        for (r = atalk_routes; r; r = r->next) {
                if (!(r->flags & RTF_UP))
@@ -459,8 +458,8 @@ static struct atalk_route *atrtr_find(struct atalk_addr *target)
                                net_route = r;
                }
        }
-       
-       /* 
+
+       /*
         * if we found a network route but not a direct host
         * route, then return it
         */
@@ -539,15 +538,15 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint)
                for (iface = atalk_interfaces; iface; iface = iface->next) {
                        if (!riface &&
                            ntohs(ga->sat_addr.s_net) >=
-                                       ntohs(iface->nets.nr_firstnet) &&
+                                       ntohs(iface->nets.nr_firstnet) &&
                            ntohs(ga->sat_addr.s_net) <=
-                                       ntohs(iface->nets.nr_lastnet))
+                                       ntohs(iface->nets.nr_lastnet))
                                riface = iface;
 
                        if (ga->sat_addr.s_net == iface->address.s_net &&
                            ga->sat_addr.s_node == iface->address.s_node)
                                riface = iface;
-               }               
+               }
                read_unlock_bh(&atalk_interfaces_lock);
 
                retval = -ENETUNREACH;
@@ -646,9 +645,14 @@ static inline void atalk_dev_down(struct net_device *dev)
 static int ddp_device_event(struct notifier_block *this, unsigned long event,
                            void *ptr)
 {
+       struct net_device *dev = ptr;
+
+       if (!net_eq(dev_net(dev), &init_net))
+               return NOTIFY_DONE;
+
        if (event == NETDEV_DOWN)
                /* Discard any use of this */
-               atalk_dev_down(ptr);
+               atalk_dev_down(dev);
 
        return NOTIFY_DONE;
 }
@@ -671,7 +675,7 @@ static int atif_ioctl(int cmd, void __user *arg)
        if (copy_from_user(&atreq, arg, sizeof(atreq)))
                return -EFAULT;
 
-       dev = __dev_get_by_name(atreq.ifr_name);
+       dev = __dev_get_by_name(&init_net, atreq.ifr_name);
        if (!dev)
                return -ENODEV;
 
@@ -700,13 +704,13 @@ static int atif_ioctl(int cmd, void __user *arg)
                         */
                        if ((dev->flags & IFF_POINTOPOINT) &&
                            atalk_find_interface(sa->sat_addr.s_net,
-                                                sa->sat_addr.s_node)) {
+                                                sa->sat_addr.s_node)) {
                                printk(KERN_DEBUG "AppleTalk: point-to-point "
                                                  "interface added with "
                                                  "existing address\n");
                                add_route = 0;
                        }
-                       
+
                        /*
                         * Phase 1 is fine on LocalTalk but we don't do
                         * EtherTalk phase 1. Anyone wanting to add it go ahead.
@@ -796,78 +800,75 @@ static int atif_ioctl(int cmd, void __user *arg)
                        sa->sat_addr.s_node = ATADDR_BCAST;
                        break;
 
-               case SIOCATALKDIFADDR:
-               case SIOCDIFADDR:
+               case SIOCATALKDIFADDR:
+               case SIOCDIFADDR:
                        if (!capable(CAP_NET_ADMIN))
                                return -EPERM;
                        if (sa->sat_family != AF_APPLETALK)
                                return -EINVAL;
                        atalk_dev_down(dev);
-                       break;                  
+                       break;
 
                case SIOCSARP:
                        if (!capable(CAP_NET_ADMIN))
-                                return -EPERM;
-                        if (sa->sat_family != AF_APPLETALK)
-                                return -EINVAL;
-                        if (!atif)
-                                return -EADDRNOTAVAIL;
-
-                        /*
-                         * for now, we only support proxy AARP on ELAP;
-                         * we should be able to do it for LocalTalk, too.
-                         */
-                        if (dev->type != ARPHRD_ETHER)
-                                return -EPROTONOSUPPORT;
-
-                        /*
-                         * atif points to the current interface on this network;
-                         * we aren't concerned about its current status (at
+                               return -EPERM;
+                       if (sa->sat_family != AF_APPLETALK)
+                               return -EINVAL;
+                       /*
+                        * for now, we only support proxy AARP on ELAP;
+                        * we should be able to do it for LocalTalk, too.
+                        */
+                       if (dev->type != ARPHRD_ETHER)
+                               return -EPROTONOSUPPORT;
+
+                       /*
+                        * atif points to the current interface on this network;
+                        * we aren't concerned about its current status (at
                         * least for now), but it has all the settings about
                         * the network we're going to probe. Consequently, it
                         * must exist.
-                         */
-                        if (!atif)
-                                return -EADDRNOTAVAIL;
-
-                        nr = (struct atalk_netrange *)&(atif->nets);
-                        /*
-                         * Phase 1 is fine on Localtalk but we don't do
-                         * Ethertalk phase 1. Anyone wanting to add it go ahead.
-                         */
-                        if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2)
-                                return -EPROTONOSUPPORT;
-
-                        if (sa->sat_addr.s_node == ATADDR_BCAST ||
+                        */
+                       if (!atif)
+                               return -EADDRNOTAVAIL;
+
+                       nr = (struct atalk_netrange *)&(atif->nets);
+                       /*
+                        * Phase 1 is fine on Localtalk but we don't do
+                        * Ethertalk phase 1. Anyone wanting to add it go ahead.
+                        */
+                       if (dev->type == ARPHRD_ETHER && nr->nr_phase != 2)
+                               return -EPROTONOSUPPORT;
+
+                       if (sa->sat_addr.s_node == ATADDR_BCAST ||
                            sa->sat_addr.s_node == 254)
-                                return -EINVAL;
-
-                        /*
-                         * Check if the chosen address is used. If so we
-                         * error and ATCP will try another.
-                         */
-                       if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0)
-                               return -EADDRINUSE;
-                       
+                               return -EINVAL;
+
                        /*
-                         * We now have an address on the local network, and
+                        * Check if the chosen address is used. If so we
+                        * error and ATCP will try another.
+                        */
+                       if (atif_proxy_probe_device(atif, &(sa->sat_addr)) < 0)
+                               return -EADDRINUSE;
+
+                       /*
+                        * We now have an address on the local network, and
                         * the AARP code will defend it for us until we take it
                         * down. We don't set up any routes right now, because
                         * ATCP will install them manually via SIOCADDRT.
-                         */
-                        break;
-
-                case SIOCDARP:
-                        if (!capable(CAP_NET_ADMIN))
-                                return -EPERM;
-                        if (sa->sat_family != AF_APPLETALK)
-                                return -EINVAL;
-                        if (!atif)
-                                return -EADDRNOTAVAIL;
-
-                        /* give to aarp module to remove proxy entry */
-                        aarp_proxy_remove(atif->dev, &(sa->sat_addr));
-                        return 0;
+                        */
+                       break;
+
+               case SIOCDARP:
+                       if (!capable(CAP_NET_ADMIN))
+                               return -EPERM;
+                       if (sa->sat_family != AF_APPLETALK)
+                               return -EINVAL;
+                       if (!atif)
+                               return -EADDRNOTAVAIL;
+
+                       /* give to aarp module to remove proxy entry */
+                       aarp_proxy_remove(atif->dev, &(sa->sat_addr));
+                       return 0;
        }
 
        return copy_to_user(arg, &atreq, sizeof(atreq)) ? -EFAULT : 0;
@@ -895,10 +896,10 @@ static int atrtr_ioctl(unsigned int cmd, void __user *arg)
                                if (copy_from_user(name, rt.rt_dev, IFNAMSIZ-1))
                                        return -EFAULT;
                                name[IFNAMSIZ-1] = '\0';
-                               dev = __dev_get_by_name(name);
+                               dev = __dev_get_by_name(&init_net, name);
                                if (!dev)
                                        return -ENODEV;
-                       }                       
+                       }
                        return atrtr_create(&rt, dev);
                }
        }
@@ -916,7 +917,7 @@ static int atrtr_ioctl(unsigned int cmd, void __user *arg)
  * Checksum: This is 'optional'. It's quite likely also a good
  * candidate for assembler hackery 8)
  */
-static unsigned long atalk_sum_partial(const unsigned char *data, 
+static unsigned long atalk_sum_partial(const unsigned char *data,
                                       int len, unsigned long sum)
 {
        /* This ought to be unwrapped neatly. I'll trust gcc for now */
@@ -937,6 +938,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
                                   int len, unsigned long sum)
 {
        int start = skb_headlen(skb);
+       struct sk_buff *frag_iter;
        int i, copy;
 
        /* checksum stuff in header space */
@@ -944,7 +946,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
                if (copy > len)
                        copy = len;
                sum = atalk_sum_partial(skb->data + offset, copy, sum);
-               if ( (len -= copy) == 0) 
+               if ( (len -= copy) == 0)
                        return sum;
 
                offset += copy;
@@ -954,7 +956,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -975,26 +977,22 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
                start = end;
        }
 
-       if (skb_shinfo(skb)->frag_list) {
-               struct sk_buff *list = skb_shinfo(skb)->frag_list;
-
-               for (; list; list = list->next) {
-                       int end;
+       skb_walk_frags(skb, frag_iter) {
+               int end;
 
-                       BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
-                       end = start + list->len;
-                       if ((copy = end - offset) > 0) {
-                               if (copy > len)
-                                       copy = len;
-                               sum = atalk_sum_skb(list, offset - start,
-                                                   copy, sum);
-                               if ((len -= copy) == 0)
-                                       return sum;
-                               offset += copy;
-                       }
-                       start = end;
+               end = start + frag_iter->len;
+               if ((copy = end - offset) > 0) {
+                       if (copy > len)
+                               copy = len;
+                       sum = atalk_sum_skb(frag_iter, offset - start,
+                                           copy, sum);
+                       if ((len -= copy) == 0)
+                               return sum;
+                       offset += copy;
                }
+               start = end;
        }
 
        BUG_ON(len > 0);
@@ -1002,7 +1000,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;
 
@@ -1010,7 +1008,7 @@ 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 = {
@@ -1023,19 +1021,23 @@ static struct proto ddp_proto = {
  * Create a socket. Initialise the socket, blank the addresses
  * set the state.
  */
-static int atalk_create(struct socket *sock, int protocol)
+static int atalk_create(struct net *net, struct socket *sock, int protocol,
+                       int kern)
 {
        struct sock *sk;
        int rc = -ESOCKTNOSUPPORT;
 
+       if (net != &init_net)
+               return -EAFNOSUPPORT;
+
        /*
         * We permit SOCK_DGRAM and RAW is an extension. It is trivial to do
-        * and gives you the full ELAP frame. Should be handy for CAP 8) 
+        * and gives you the full ELAP frame. Should be handy for CAP 8)
         */
        if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
                goto out;
        rc = -ENOMEM;
-       sk = sk_alloc(PF_APPLETALK, GFP_KERNEL, &ddp_proto, 1);
+       sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto);
        if (!sk)
                goto out;
        rc = 0;
@@ -1053,11 +1055,13 @@ static int atalk_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
 
+       lock_kernel();
        if (sk) {
                sock_orphan(sk);
                sock->sk = NULL;
                atalk_destroy_socket(sk);
        }
+       unlock_kernel();
        return 0;
 }
 
@@ -1133,6 +1137,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        struct sockaddr_at *addr = (struct sockaddr_at *)uaddr;
        struct sock *sk = sock->sk;
        struct atalk_sock *at = at_sk(sk);
+       int err;
 
        if (!sock_flag(sk, SOCK_ZAPPED) ||
            addr_len != sizeof(struct sockaddr_at))
@@ -1141,37 +1146,44 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        if (addr->sat_family != AF_APPLETALK)
                return -EAFNOSUPPORT;
 
+       lock_kernel();
        if (addr->sat_addr.s_net == htons(ATADDR_ANYNET)) {
                struct atalk_addr *ap = atalk_find_primary();
 
+               err = -EADDRNOTAVAIL;
                if (!ap)
-                       return -EADDRNOTAVAIL;
+                       goto out;
 
                at->src_net  = addr->sat_addr.s_net = ap->s_net;
                at->src_node = addr->sat_addr.s_node= ap->s_node;
        } else {
+               err = -EADDRNOTAVAIL;
                if (!atalk_find_interface(addr->sat_addr.s_net,
                                          addr->sat_addr.s_node))
-                       return -EADDRNOTAVAIL;
+                       goto out;
 
                at->src_net  = addr->sat_addr.s_net;
                at->src_node = addr->sat_addr.s_node;
        }
 
        if (addr->sat_port == ATADDR_ANYPORT) {
-               int n = atalk_pick_and_bind_port(sk, addr);
+               err = atalk_pick_and_bind_port(sk, addr);
 
-               if (n < 0)
-                       return n;
+               if (err < 0)
+                       goto out;
        } else {
                at->src_port = addr->sat_port;
 
+               err = -EADDRINUSE;
                if (atalk_find_or_insert_socket(sk, addr))
-                       return -EADDRINUSE;
+                       goto out;
        }
 
        sock_reset_flag(sk, SOCK_ZAPPED);
-       return 0;
+       err = 0;
+out:
+       unlock_kernel();
+       return err;
 }
 
 /* Set the address we talk to */
@@ -1181,6 +1193,7 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
        struct sock *sk = sock->sk;
        struct atalk_sock *at = at_sk(sk);
        struct sockaddr_at *addr;
+       int err;
 
        sk->sk_state   = TCP_CLOSE;
        sock->state = SS_UNCONNECTED;
@@ -1195,22 +1208,25 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
 
        if (addr->sat_addr.s_node == ATADDR_BCAST &&
            !sock_flag(sk, SOCK_BROADCAST)) {
-#if 1  
+#if 1
                printk(KERN_WARNING "%s is broken and did not set "
                                    "SO_BROADCAST. It will break when 2.2 is "
                                    "released.\n",
                        current->comm);
 #else
                return -EACCES;
-#endif                 
+#endif
        }
 
+       lock_kernel();
+       err = -EBUSY;
        if (sock_flag(sk, SOCK_ZAPPED))
                if (atalk_autobind(sk) < 0)
-                       return -EBUSY;
+                       goto out;
 
+       err = -ENETUNREACH;
        if (!atrtr_get_dev(&addr->sat_addr))
-               return -ENETUNREACH;
+               goto out;
 
        at->dest_port = addr->sat_port;
        at->dest_net  = addr->sat_addr.s_net;
@@ -1218,7 +1234,10 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
 
        sock->state  = SS_CONNECTED;
        sk->sk_state = TCP_ESTABLISHED;
-       return 0;
+       err = 0;
+out:
+       unlock_kernel();
+       return err;
 }
 
 /*
@@ -1231,16 +1250,21 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
        struct sockaddr_at sat;
        struct sock *sk = sock->sk;
        struct atalk_sock *at = at_sk(sk);
+       int err;
 
+       lock_kernel();
+       err = -ENOBUFS;
        if (sock_flag(sk, SOCK_ZAPPED))
                if (atalk_autobind(sk) < 0)
-                       return -ENOBUFS;
+                       goto out;
 
        *uaddr_len = sizeof(struct sockaddr_at);
+       memset(&sat.sat_zero, 0, sizeof(sat.sat_zero));
 
        if (peer) {
+               err = -ENOTCONN;
                if (sk->sk_state != TCP_ESTABLISHED)
-                       return -ENOTCONN;
+                       goto out;
 
                sat.sat_addr.s_net  = at->dest_net;
                sat.sat_addr.s_node = at->dest_node;
@@ -1251,36 +1275,51 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
                sat.sat_port        = at->src_port;
        }
 
+       err = 0;
        sat.sat_family = AF_APPLETALK;
        memcpy(uaddr, &sat, sizeof(sat));
-       return 0;
+
+out:
+       unlock_kernel();
+       return err;
+}
+
+static unsigned int atalk_poll(struct file *file, struct socket *sock,
+                          poll_table *wait)
+{
+       int err;
+       lock_kernel();
+       err = datagram_poll(file, sock, wait);
+       unlock_kernel();
+       return err;
 }
 
 #if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE)
 static __inline__ int is_ip_over_ddp(struct sk_buff *skb)
 {
-        return skb->data[12] == 22;
+       return skb->data[12] == 22;
 }
 
 static int handle_ip_over_ddp(struct sk_buff *skb)
 {
-        struct net_device *dev = __dev_get_by_name("ipddp0");
+       struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0");
        struct net_device_stats *stats;
 
        /* This needs to be able to handle ipddp"N" devices */
-        if (!dev)
-                return -ENODEV;
-
-        skb->protocol = htons(ETH_P_IP);
-        skb_pull(skb, 13);
-        skb->dev   = dev;
-        skb->h.raw = skb->data;
-
-       stats = dev->priv;
-        stats->rx_packets++;
-        stats->rx_bytes += skb->len + 13;
-        netif_rx(skb);  /* Send the SKB up to a higher place. */
-       return 0;
+       if (!dev) {
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
+       skb->protocol = htons(ETH_P_IP);
+       skb_pull(skb, 13);
+       skb->dev   = dev;
+       skb_reset_transport_header(skb);
+
+       stats = netdev_priv(dev);
+       stats->rx_packets++;
+       stats->rx_bytes += skb->len + 13;
+       return netif_rx(skb);  /* Send the SKB up to a higher place. */
 }
 #else
 /* make it easy for gcc to optimize this test out, i.e. kill the code */
@@ -1288,16 +1327,15 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
 #define handle_ip_over_ddp(skb) 0
 #endif
 
-static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
-                              struct ddpehdr *ddp, struct ddpebits *ddphv,
-                              int origlen)
+static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
+                             struct ddpehdr *ddp, __u16 len_hops, int origlen)
 {
        struct atalk_route *rt;
        struct atalk_addr ta;
 
        /*
         * Don't route multicast, etc., packets, or packets sent to "this
-        * network" 
+        * network"
         */
        if (skb->pkt_type != PACKET_HOST || !ddp->deh_dnet) {
                /*
@@ -1317,10 +1355,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
@@ -1332,14 +1372,13 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
                ta.s_node = rt->gateway.s_node;
        }
 
-        /* Fix up skb->len field */
-        skb_trim(skb, min_t(unsigned int, origlen,
+       /* 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
@@ -1356,22 +1395,24 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
                /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */
                struct sk_buff *nskb = skb_realloc_headroom(skb, 32);
                kfree_skb(skb);
-               if (!nskb) 
-                       goto out;
                skb = nskb;
        } else
                skb = skb_unshare(skb, GFP_ATOMIC);
-       
+
        /*
         * If the buffer didn't vanish into the lack of space bitbucket we can
         * send it.
         */
-       if (skb && aarp_send_ddp(rt->dev, skb, &ta, NULL) == -1)
-               goto free_it;
-out:
-       return;
+       if (skb == NULL)
+               goto drop;
+
+       if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP)
+               return NET_RX_DROP;
+       return NET_RX_SUCCESS;
 free_it:
        kfree_skb(skb);
+drop:
+       return NET_RX_DROP;
 }
 
 /**
@@ -1381,10 +1422,10 @@ free_it:
  *     @pt - packet type
  *
  *     Receive a packet (in skb) from device dev. This has come from the SNAP
- *     decoder, and on entry skb->h.raw is the DDP header, skb->len is the DDP
- *     header, skb->len is the DDP length. The physical headers have been
- *     extracted. PPP should probably pass frames marked as for this layer.
- *     [ie ARPHRD_ETHERTALK]
+ *     decoder, and on entry skb->transport_header is the DDP header, skb->len
+ *     is the DDP header, skb->len is the DDP length. The physical headers
+ *     have been extracted. PPP should probably pass frames marked as for this
+ *     layer.  [ie ARPHRD_ETHERTALK]
  */
 static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
                     struct packet_type *pt, struct net_device *orig_dev)
@@ -1393,46 +1434,47 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
        struct sock *sock;
        struct atalk_iface *atif;
        struct sockaddr_at tosat;
-        int origlen;
-        struct ddpebits ddphv;
+       int origlen;
+       __u16 len_hops;
+
+       if (!net_eq(dev_net(dev), &init_net))
+               goto drop;
 
        /* Don't mangle buffer if shared */
-       if (!(skb = skb_share_check(skb, GFP_ATOMIC))) 
+       if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
                goto out;
-               
+
        /* Size check and make sure header is contiguous */
        if (!pskb_may_pull(skb, sizeof(*ddp)))
-               goto freeit;
+               goto drop;
 
        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))
-               goto freeit;
+       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 drop;
+       }
 
        /*
         * 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;
+               goto drop;
 
        /* Check the packet is aimed at us */
        if (!ddp->deh_dnet)     /* Net 0 is 'this network' */
@@ -1444,8 +1486,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
                /* Not ours, so we route the packet via the correct
                 * AppleTalk iface
                 */
-               atalk_route_packet(skb, dev, ddp, &ddphv, origlen);
-               goto out;
+               return atalk_route_packet(skb, dev, ddp, len_hops, origlen);
        }
 
        /* if IP over DDP is not selected this code will be optimized out */
@@ -1461,18 +1502,21 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
 
        sock = atalk_search_socket(&tosat, atif);
        if (!sock) /* But not one of our sockets */
-               goto freeit;
+               goto drop;
 
        /* Queue packet (standard) */
        skb->sk = sock;
 
        if (sock_queue_rcv_skb(sock, skb) < 0)
-               goto freeit;
-out:
-       return 0;
-freeit:
+               goto drop;
+
+       return NET_RX_SUCCESS;
+
+drop:
        kfree_skb(skb);
-       goto out;
+out:
+       return NET_RX_DROP;
+
 }
 
 /*
@@ -1483,17 +1527,20 @@ freeit:
 static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
                     struct packet_type *pt, struct net_device *orig_dev)
 {
+       if (!net_eq(dev_net(dev), &init_net))
+               goto freeit;
+
        /* Expand any short form frames */
-       if (skb->mac.raw[2] == 1) {
+       if (skb_mac_header(skb)[2] == 1) {
                struct ddpehdr *ddp;
                /* 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 */
-               if (!(skb = skb_share_check(skb, GFP_ATOMIC))) 
+               if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
                        return 0;
 
                /*
@@ -1504,14 +1551,14 @@ static int ltalk_rcv(struct sk_buff *skb, struct net_device *dev,
 
                /* Now fill in the long header */
 
-               /*
-                * These two first. The mac overlays the new source/dest
-                * network information so we MUST copy these before
-                * we write the network numbers !
-                */
+               /*
+                * These two first. The mac overlays the new source/dest
+                * network information so we MUST copy these before
+                * we write the network numbers !
+                */
 
-               ddp->deh_dnode = skb->mac.raw[0];     /* From physical header */
-               ddp->deh_snode = skb->mac.raw[1];     /* From physical header */
+               ddp->deh_dnode = skb_mac_header(skb)[0];     /* From physical header */
+               ddp->deh_snode = skb_mac_header(skb)[1];     /* From physical header */
 
                ddp->deh_dnet  = ap->s_net;     /* Network number */
                ddp->deh_snet  = ap->s_net;
@@ -1519,13 +1566,10 @@ 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;
+       skb_reset_transport_header(skb);
 
        return atalk_rcv(skb, dev, pt, orig_dev);
 freeit:
@@ -1555,27 +1599,28 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        if (len > DDP_MAXSZ)
                return -EMSGSIZE;
 
+       lock_kernel();
        if (usat) {
+               err = -EBUSY;
                if (sock_flag(sk, SOCK_ZAPPED))
                        if (atalk_autobind(sk) < 0)
-                               return -EBUSY;
+                               goto out;
 
+               err = -EINVAL;
                if (msg->msg_namelen < sizeof(*usat) ||
                    usat->sat_family != AF_APPLETALK)
-                       return -EINVAL;
+                       goto out;
 
-               /* netatalk doesn't implement this check */
+               err = -EPERM;
+               /* netatalk didn't implement this check */
                if (usat->sat_addr.s_node == ATADDR_BCAST &&
                    !sock_flag(sk, SOCK_BROADCAST)) {
-                       printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as "
-                                        "it will break before 2.2\n");
-#if 0
-                       return -EPERM;
-#endif
+                       goto out;
                }
        } else {
+               err = -ENOTCONN;
                if (sk->sk_state != TCP_ESTABLISHED)
-                       return -ENOTCONN;
+                       goto out;
                usat = &local_satalk;
                usat->sat_family      = AF_APPLETALK;
                usat->sat_port        = at->dest_port;
@@ -1591,7 +1636,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);
-               dev = rt->dev;
        } else {
                struct atalk_addr at_hint;
 
@@ -1599,10 +1643,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                at_hint.s_net  = at->src_net;
 
                rt = atrtr_find(&at_hint);
-               dev = rt->dev;
        }
+       err = ENETUNREACH;
        if (!rt)
-               return -ENETUNREACH;
+               goto out;
 
        dev = rt->dev;
 
@@ -1612,8 +1656,8 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        size += dev->hard_header_len;
        skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
        if (!skb)
-               return err;
-       
+               goto out;
+
        skb->sk = sk;
        skb_reserve(skb, ddp_dl->header_length);
        skb_reserve(skb, dev->hard_header_len);
@@ -1622,16 +1666,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;
@@ -1644,7 +1679,8 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
        if (err) {
                kfree_skb(skb);
-               return -EFAULT;
+               err = -EFAULT;
+               goto out;
        }
 
        if (sk->sk_no_check == 1)
@@ -1663,10 +1699,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                if (skb2) {
                        loopback = 1;
                        SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk);
-                       if (aarp_send_ddp(dev, skb2,
-                                         &usat->sat_addr, NULL) == -1)
-                               kfree_skb(skb2);
-                               /* else queued/sent above in the aarp queue */
+                       /*
+                        * If it fails it is queued/sent above in the aarp queue
+                        */
+                       aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL);
                }
        }
 
@@ -1683,7 +1719,8 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                        rt = atrtr_find(&at_lo);
                        if (!rt) {
                                kfree_skb(skb);
-                               return -ENETUNREACH;
+                               err = -ENETUNREACH;
+                               goto out;
                        }
                        dev = rt->dev;
                        skb->dev = dev;
@@ -1696,13 +1733,16 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                    usat = &gsat;
                }
 
-               if (aarp_send_ddp(dev, skb, &usat->sat_addr, NULL) == -1)
-                       kfree_skb(skb);
-               /* else queued/sent above in the aarp queue */
+               /*
+                * If it fails it is queued/sent above in the aarp queue
+                */
+               aarp_send_ddp(dev, skb, &usat->sat_addr, NULL);
        }
        SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len);
 
-       return len;
+out:
+       unlock_kernel();
+       return err ? : len;
 }
 
 static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
@@ -1712,34 +1752,30 @@ 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,
+       struct sk_buff *skb;
+
+       lock_kernel();
+       skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                                flags & MSG_DONTWAIT, &err);
        if (!skb)
-               return err;
+               goto out;
 
        /* 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) {
@@ -1752,6 +1788,9 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
        }
 
        skb_free_datagram(sk, skb);     /* Free the datagram. */
+
+out:
+       unlock_kernel();
        return err ? : copied;
 }
 
@@ -1768,8 +1807,7 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        switch (cmd) {
                /* Protocol layer */
                case TIOCOUTQ: {
-                       long amount = sk->sk_sndbuf -
-                                     atomic_read(&sk->sk_wmem_alloc);
+                       long amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
 
                        if (amount < 0)
                                amount = 0;
@@ -1792,6 +1830,9 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCGSTAMP:
                        rc = sock_get_timestamp(sk, argp);
                        break;
+               case SIOCGSTAMPNS:
+                       rc = sock_get_timestampns(sk, argp);
+                       break;
                /* Routing */
                case SIOCADDRT:
                case SIOCDELRT:
@@ -1821,24 +1862,26 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 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.
+        * SIOCATALKDIFADDR is a SIOCPROTOPRIVATE ioctl number, so we
+        * cannot handle it in common code. The data we access if ifreq
+        * here is compatible, so we can simply call the native
+        * handler.
         */
+       if (cmd == SIOCATALKDIFADDR)
+               return atalk_ioctl(sock, cmd, (unsigned long)compat_ptr(arg));
+
        return -ENOIOCTLCMD;
 }
 #endif
 
 
-static struct net_proto_family atalk_family_ops = {
+static const struct net_proto_family atalk_family_ops = {
        .family         = PF_APPLETALK,
        .create         = atalk_create,
        .owner          = THIS_MODULE,
 };
 
-static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
+static const struct proto_ops atalk_dgram_ops = {
        .family         = PF_APPLETALK,
        .owner          = THIS_MODULE,
        .release        = atalk_release,
@@ -1847,7 +1890,7 @@ static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
        .socketpair     = sock_no_socketpair,
        .accept         = sock_no_accept,
        .getname        = atalk_getname,
-       .poll           = datagram_poll,
+       .poll           = atalk_poll,
        .ioctl          = atalk_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = atalk_compat_ioctl,
@@ -1862,31 +1905,27 @@ static const struct proto_ops SOCKOPS_WRAPPED(atalk_dgram_ops) = {
        .sendpage       = sock_no_sendpage,
 };
 
-#include <linux/smp_lock.h>
-SOCKOPS_WRAP(atalk_dgram, PF_APPLETALK);
-
 static struct notifier_block ddp_notifier = {
        .notifier_call  = ddp_device_event,
 };
 
-static struct packet_type ltalk_packet_type = {
-       .type           = __constant_htons(ETH_P_LOCALTALK),
+static struct packet_type ltalk_packet_type __read_mostly = {
+       .type           = cpu_to_be16(ETH_P_LOCALTALK),
        .func           = ltalk_rcv,
 };
 
-static struct packet_type ppptalk_packet_type = {
-       .type           = __constant_htons(ETH_P_PPPTALK),
+static struct packet_type ppptalk_packet_type __read_mostly = {
+       .type           = cpu_to_be16(ETH_P_PPPTALK),
        .func           = atalk_rcv,
 };
 
 static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B };
 
 /* Export symbols for use by drivers when AppleTalk is a module */
-EXPORT_SYMBOL(aarp_send_ddp);
 EXPORT_SYMBOL(atrtr_get_dev);
 EXPORT_SYMBOL(atalk_find_dev_addr);
 
-static char atalk_err_snap[] __initdata =
+static const char atalk_err_snap[] __initconst =
        KERN_CRIT "Unable to register DDP with SNAP.\n";
 
 /* Called by proto.c on kernel start up */
@@ -1940,6 +1979,6 @@ static void __exit atalk_exit(void)
 module_exit(atalk_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>");
+MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
 MODULE_DESCRIPTION("AppleTalk 0.20\n");
 MODULE_ALIAS_NETPROTO(PF_APPLETALK);