appletalk: Fix skb allocation size in loopback case
[linux-2.6-block.git] / net / appletalk / ddp.c
index ca1a0d07a087813467cc67b5a05bf9826f7fc992..ebda397fa95a7d565d51e022a8386f64d29e56a2 100644 (file)
@@ -1577,8 +1577,8 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
        struct sk_buff *skb;
        struct net_device *dev;
        struct ddpehdr *ddp;
-       int size;
-       struct atalk_route *rt;
+       int size, hard_header_len;
+       struct atalk_route *rt, *rt_lo = NULL;
        int err;
 
        if (flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
@@ -1641,7 +1641,22 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
        SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n",
                        sk, size, dev->name);
 
-       size += dev->hard_header_len;
+       hard_header_len = dev->hard_header_len;
+       /* Leave room for loopback hardware header if necessary */
+       if (usat->sat_addr.s_node == ATADDR_BCAST &&
+           (dev->flags & IFF_LOOPBACK || !(rt->flags & RTF_GATEWAY))) {
+               struct atalk_addr at_lo;
+
+               at_lo.s_node = 0;
+               at_lo.s_net  = 0;
+
+               rt_lo = atrtr_find(&at_lo);
+
+               if (rt_lo && rt_lo->dev->hard_header_len > hard_header_len)
+                       hard_header_len = rt_lo->dev->hard_header_len;
+       }
+
+       size += hard_header_len;
        release_sock(sk);
        skb = sock_alloc_send_skb(sk, size, (flags & MSG_DONTWAIT), &err);
        lock_sock(sk);
@@ -1649,7 +1664,7 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
                goto out;
 
        skb_reserve(skb, ddp_dl->header_length);
-       skb_reserve(skb, dev->hard_header_len);
+       skb_reserve(skb, hard_header_len);
        skb->dev = dev;
 
        SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
@@ -1700,18 +1715,12 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
                /* 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) {
+                       if (!rt_lo) {
                                kfree_skb(skb);
                                err = -ENETUNREACH;
                                goto out;
                        }
-                       dev = rt->dev;
+                       dev = rt_lo->dev;
                        skb->dev = dev;
                }
                ddp_dl->request(ddp_dl, skb, dev->dev_addr);