]> err.no Git - linux-2.6/blobdiff - net/ipv6/raw.c
Merge refs/heads/ieee80211-wifi from master.kernel.org:/pub/scm/linux/kernel/git...
[linux-2.6] / net / ipv6 / raw.c
index 5488ad0de4f6bd227665657fc65b7fce4fafcca4..7a5863298f3f8efe49977a9a7d39edf5fba71442 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <asm/uaccess.h>
 #include <asm/ioctls.h>
+#include <asm/bug.h>
 
 #include <net/ip.h>
 #include <net/sock.h>
@@ -48,6 +49,7 @@
 #include <net/transp_v6.h>
 #include <net/udp.h>
 #include <net/inet_common.h>
+#include <net/tcp_states.h>
 
 #include <net/rawv6.h>
 #include <net/xfrm.h>
@@ -80,7 +82,8 @@ static void raw_v6_unhash(struct sock *sk)
 
 /* Grumble... icmp and ip_input want to get at this... */
 struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
-                            struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+                            struct in6_addr *loc_addr, struct in6_addr *rmt_addr,
+                            int dif)
 {
        struct hlist_node *node;
        int is_multicast = ipv6_addr_is_multicast(loc_addr);
@@ -93,6 +96,9 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
                            !ipv6_addr_equal(&np->daddr, rmt_addr))
                                continue;
 
+                       if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
+                               continue;
+
                        if (!ipv6_addr_any(&np->rcv_saddr)) {
                                if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
                                        goto found;
@@ -136,11 +142,12 @@ static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
  *
  *     Caller owns SKB so we must make clones.
  */
-void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
+int ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
 {
        struct in6_addr *saddr;
        struct in6_addr *daddr;
        struct sock *sk;
+       int delivered = 0;
        __u8 hash;
 
        saddr = &skb->nh.ipv6h->saddr;
@@ -159,9 +166,10 @@ void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
        if (sk == NULL)
                goto out;
 
-       sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
+       sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr, skb->dev->ifindex);
 
        while (sk) {
+               delivered = 1;
                if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) {
                        struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
 
@@ -169,10 +177,12 @@ void ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
                        if (clone)
                                rawv6_rcv(sk, clone);
                }
-               sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr);
+               sk = __raw_v6_lookup(sk_next(sk), nexthdr, daddr, saddr,
+                                    skb->dev->ifindex);
        }
 out:
        read_unlock(&raw_v6_lock);
+       return delivered;
 }
 
 /* This cleans up af_inet6 a bit. -DaveM */
@@ -327,12 +337,13 @@ int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
 
        if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
                if (skb->ip_summed == CHECKSUM_HW) {
+                       skb_postpull_rcsum(skb, skb->nh.raw,
+                                          skb->h.raw - skb->nh.raw);
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                        if (csum_ipv6_magic(&skb->nh.ipv6h->saddr,
                                            &skb->nh.ipv6h->daddr,
                                            skb->len, inet->num, skb->csum)) {
-                               LIMIT_NETDEBUG(
-                               printk(KERN_DEBUG "raw v6 hw csum failure.\n"));
+                               LIMIT_NETDEBUG(KERN_DEBUG "raw v6 hw csum failure.\n");
                                skb->ip_summed = CHECKSUM_NONE;
                        }
                }
@@ -433,12 +444,12 @@ csum_copy_err:
        /* Clear queue. */
        if (flags&MSG_PEEK) {
                int clear = 0;
-               spin_lock_irq(&sk->sk_receive_queue.lock);
+               spin_lock_bh(&sk->sk_receive_queue.lock);
                if (skb == skb_peek(&sk->sk_receive_queue)) {
                        __skb_unlink(skb, &sk->sk_receive_queue);
                        clear = 1;
                }
-               spin_unlock_irq(&sk->sk_receive_queue.lock);
+               spin_unlock_bh(&sk->sk_receive_queue.lock);
                if (clear)
                        kfree_skb(skb);
        }
@@ -452,12 +463,15 @@ csum_copy_err:
 }
 
 static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
-                                    struct raw6_sock *rp, int len)
+                                    struct raw6_sock *rp)
 {
        struct sk_buff *skb;
        int err = 0;
-       u16 *csum;
+       int offset;
+       int len;
+       int total_len;
        u32 tmp_csum;
+       u16 csum;
 
        if (!rp->checksum)
                goto send;
@@ -465,10 +479,11 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
        if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
                goto out;
 
-       if (rp->offset + 1 < len)
-               csum = (u16 *)(skb->h.raw + rp->offset);
-       else {
+       offset = rp->offset;
+       total_len = inet_sk(sk)->cork.length - (skb->nh.raw - skb->data);
+       if (offset >= total_len - 1) {
                err = -EINVAL;
+               ip6_flush_pending_frames(sk);
                goto out;
        }
 
@@ -479,23 +494,46 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi *fl,
                 */
                tmp_csum = skb->csum;
        } else {
+               struct sk_buff *csum_skb = NULL;
                tmp_csum = 0;
 
                skb_queue_walk(&sk->sk_write_queue, skb) {
                        tmp_csum = csum_add(tmp_csum, skb->csum);
+
+                       if (csum_skb)
+                               continue;
+
+                       len = skb->len - (skb->h.raw - skb->data);
+                       if (offset >= len) {
+                               offset -= len;
+                               continue;
+                       }
+
+                       csum_skb = skb;
                }
+
+               skb = csum_skb;
        }
 
+       offset += skb->h.raw - skb->data;
+       if (skb_copy_bits(skb, offset, &csum, 2))
+               BUG();
+
        /* in case cksum was not initialized */
-       if (unlikely(*csum))
-               tmp_csum = csum_sub(tmp_csum, *csum);
+       if (unlikely(csum))
+               tmp_csum = csum_sub(tmp_csum, csum);
+
+       tmp_csum = csum_ipv6_magic(&fl->fl6_src,
+                                  &fl->fl6_dst,
+                                  total_len, fl->proto, tmp_csum);
 
-       *csum = csum_ipv6_magic(&fl->fl6_src,
-                               &fl->fl6_dst,
-                               len, fl->proto, tmp_csum);
+       if (tmp_csum == 0)
+               tmp_csum = -1;
+
+       csum = tmp_csum;
+       if (skb_store_bits(skb, offset, &csum, 2))
+               BUG();
 
-       if (*csum == 0)
-               *csum = -1;
 send:
        err = ip6_push_pending_frames(sk);
 out:
@@ -506,7 +544,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
                        struct flowi *fl, struct rt6_info *rt, 
                        unsigned int flags)
 {
-       struct inet_sock *inet = inet_sk(sk);
+       struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6hdr *iph;
        struct sk_buff *skb;
        unsigned int hh_len;
@@ -543,7 +581,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
        err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
                      dst_output);
        if (err > 0)
-               err = inet->recverr ? net_xmit_errno(err) : 0;
+               err = np->recverr ? net_xmit_errno(err) : 0;
        if (err)
                goto error;
 out:
@@ -774,14 +812,12 @@ back_from_confirm:
                if (err)
                        ip6_flush_pending_frames(sk);
                else if (!(msg->msg_flags & MSG_MORE))
-                       err = rawv6_push_pending_frames(sk, &fl, rp, len);
+                       err = rawv6_push_pending_frames(sk, &fl, rp);
        }
 done:
        ip6_dst_store(sk, dst,
                      ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ?
                      &np->daddr : NULL);
-       if (err > 0)
-               err = np->recverr ? net_xmit_errno(err) : 0;
 
        release_sock(sk);
 out:   
@@ -945,11 +981,11 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg)
                        struct sk_buff *skb;
                        int amount = 0;
 
-                       spin_lock_irq(&sk->sk_receive_queue.lock);
+                       spin_lock_bh(&sk->sk_receive_queue.lock);
                        skb = skb_peek(&sk->sk_receive_queue);
                        if (skb != NULL)
                                amount = skb->tail - skb->h.raw;
-                       spin_unlock_irq(&sk->sk_receive_queue.lock);
+                       spin_unlock_bh(&sk->sk_receive_queue.lock);
                        return put_user(amount, (int __user *)arg);
                }