net: igmp: fix source address check for IGMPv3 reports
[pandora-kernel.git] / net / core / neighbour.c
index 5ac07d3..c8c2645 100644 (file)
@@ -237,7 +237,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
                                   we must kill timers etc. and move
                                   it to safe state.
                                 */
-                               skb_queue_purge(&n->arp_queue);
+                               __skb_queue_purge(&n->arp_queue);
                                n->output = neigh_blackhole;
                                if (n->nud_state & NUD_VALID)
                                        n->nud_state = NUD_NOARP;
@@ -291,7 +291,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl)
        if (!n)
                goto out_entries;
 
-       skb_queue_head_init(&n->arp_queue);
+       __skb_queue_head_init(&n->arp_queue);
        rwlock_init(&n->lock);
        seqlock_init(&n->ha_lock);
        n->updated        = n->used = now;
@@ -701,7 +701,9 @@ void neigh_destroy(struct neighbour *neigh)
        if (neigh_del_timer(neigh))
                printk(KERN_WARNING "Impossible event.\n");
 
-       skb_queue_purge(&neigh->arp_queue);
+       write_lock_bh(&neigh->lock);
+       __skb_queue_purge(&neigh->arp_queue);
+       write_unlock_bh(&neigh->lock);
 
        dev_put(neigh->dev);
        neigh_parms_put(neigh->parms);
@@ -802,6 +804,8 @@ next_elt:
                write_unlock_bh(&tbl->lock);
                cond_resched();
                write_lock_bh(&tbl->lock);
+               nht = rcu_dereference_protected(tbl->nht,
+                                               lockdep_is_held(&tbl->lock));
        }
        /* Cycle through all hash buckets every base_reachable_time/2 ticks.
         * ARP entry timeouts range from 1/2 base_reachable_time to 3/2
@@ -841,7 +845,7 @@ static void neigh_invalidate(struct neighbour *neigh)
                neigh->ops->error_report(neigh, skb);
                write_lock(&neigh->lock);
        }
-       skb_queue_purge(&neigh->arp_queue);
+       __skb_queue_purge(&neigh->arp_queue);
 }
 
 static void neigh_probe(struct neighbour *neigh)
@@ -951,6 +955,8 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
        rc = 0;
        if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
                goto out_unlock_bh;
+       if (neigh->dead)
+               goto out_dead;
 
        if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
                if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
@@ -999,6 +1005,13 @@ out_unlock_bh:
                write_unlock(&neigh->lock);
        local_bh_enable();
        return rc;
+
+out_dead:
+       if (neigh->nud_state & NUD_STALE)
+               goto out_unlock_bh;
+       write_unlock_bh(&neigh->lock);
+       kfree_skb(skb);
+       return 1;
 }
 EXPORT_SYMBOL(__neigh_event_send);
 
@@ -1062,6 +1075,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
        if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
            (old & (NUD_NOARP | NUD_PERMANENT)))
                goto out;
+       if (neigh->dead)
+               goto out;
 
        if (!(new & NUD_VALID)) {
                neigh_del_timer(neigh);
@@ -1174,7 +1189,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
 
                        write_lock_bh(&neigh->lock);
                }
-               skb_queue_purge(&neigh->arp_queue);
+               __skb_queue_purge(&neigh->arp_queue);
        }
 out:
        if (update_isrouter) {
@@ -1252,8 +1267,6 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
        if (!dst)
                goto discard;
 
-       __skb_pull(skb, skb_network_offset(skb));
-
        if (!neigh_event_send(neigh, skb)) {
                int err;
                struct net_device *dev = neigh->dev;
@@ -1263,6 +1276,7 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
                        neigh_hh_init(neigh, dst);
 
                do {
+                       __skb_pull(skb, skb_network_offset(skb));
                        seq = read_seqbegin(&neigh->ha_lock);
                        err = dev_hard_header(skb, dev, ntohs(skb->protocol),
                                              neigh->ha, NULL, skb->len);
@@ -1293,9 +1307,8 @@ int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb)
        unsigned int seq;
        int err;
 
-       __skb_pull(skb, skb_network_offset(skb));
-
        do {
+               __skb_pull(skb, skb_network_offset(skb));
                seq = read_seqbegin(&neigh->ha_lock);
                err = dev_hard_header(skb, dev, ntohs(skb->protocol),
                                      neigh->ha, NULL, skb->len);