]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.0-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Feb 2012 21:11:01 +0000 (13:11 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Feb 2012 21:11:01 +0000 (13:11 -0800)
added patches:
0001-net-fix-NULL-dereferences-in-check_peer_redir.patch

queue-3.0/0001-net-fix-NULL-dereferences-in-check_peer_redir.patch [new file with mode: 0644]
queue-3.0/series

diff --git a/queue-3.0/0001-net-fix-NULL-dereferences-in-check_peer_redir.patch b/queue-3.0/0001-net-fix-NULL-dereferences-in-check_peer_redir.patch
new file mode 100644 (file)
index 0000000..2f2245a
--- /dev/null
@@ -0,0 +1,1579 @@
+From db098b337e98ba2624c2aed1148f03c854d27bb5 Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <eric.dumazet@gmail.com>
+Date: Thu, 9 Feb 2012 16:13:19 -0500
+Subject: [PATCH] net: fix NULL dereferences in check_peer_redir()
+
+From: Eric Dumazet <eric.dumazet@gmail.com>
+
+[ Upstream commit d3aaeb38c40e5a6c08dd31a1b64da65c4352be36, along
+  with dependent backports of commits:
+     69cce1d1404968f78b177a0314f5822d5afdbbfb
+     9de79c127cccecb11ae6a21ab1499e87aa222880
+     218fa90f072e4aeff9003d57e390857f4f35513e
+     580da35a31f91a594f3090b7a2c39b85cb051a12
+     f7e57044eeb1841847c24aa06766c8290c202583
+     e049f28883126c689cf95859480d9ee4ab23b7fa ]
+
+Gergely Kalman reported crashes in check_peer_redir().
+
+It appears commit f39925dbde778 (ipv4: Cache learned redirect
+information in inetpeer.) added a race, leading to possible NULL ptr
+dereference.
+
+Since we can now change dst neighbour, we should make sure a reader can
+safely use a neighbour.
+
+Add RCU protection to dst neighbour, and make sure check_peer_redir()
+can be called safely by different cpus in parallel.
+
+As neighbours are already freed after one RCU grace period, this patch
+should not add typical RCU penalty (cache cold effects)
+
+Many thanks to Gergely for providing a pretty report pointing to the
+bug.
+
+Reported-by: Gergely Kalman <synapse@hippy.csoma.elte.hu>
+Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/infiniband/core/addr.c                 |   16 ++++--
+ drivers/infiniband/hw/cxgb3/iwch_cm.c          |   16 +++++-
+ drivers/infiniband/hw/cxgb4/cm.c               |   46 ++++++++++---------
+ drivers/infiniband/hw/mlx4/qp.c                |    2 
+ drivers/infiniband/hw/nes/nes_cm.c             |    8 ++-
+ drivers/infiniband/ulp/ipoib/ipoib_main.c      |   59 ++++++++++++++++---------
+ drivers/infiniband/ulp/ipoib/ipoib_multicast.c |   24 ++++++----
+ drivers/net/cxgb3/cxgb3_offload.c              |    8 +--
+ drivers/s390/net/qeth_l3_main.c                |   23 +++++++--
+ drivers/scsi/cxgbi/cxgb3i/cxgb3i.c             |    2 
+ drivers/scsi/cxgbi/cxgb4i/cxgb4i.c             |    2 
+ drivers/scsi/cxgbi/libcxgbi.c                  |    4 -
+ include/net/arp.h                              |    1 
+ include/net/dst.h                              |   27 ++++++++++-
+ net/atm/clip.c                                 |   16 ++++--
+ net/bridge/br_netfilter.c                      |    6 +-
+ net/core/dst.c                                 |   15 ++++--
+ net/core/neighbour.c                           |   19 +++++---
+ net/decnet/dn_neigh.c                          |    8 +--
+ net/decnet/dn_route.c                          |   18 ++++---
+ net/ipv4/arp.c                                 |   28 +++++++----
+ net/ipv4/ip_gre.c                              |    2 
+ net/ipv4/ip_output.c                           |   22 +++++++--
+ net/ipv4/route.c                               |   31 ++++++++-----
+ net/ipv6/addrconf.c                            |    2 
+ net/ipv6/ip6_fib.c                             |    2 
+ net/ipv6/ip6_output.c                          |   40 +++++++++++++---
+ net/ipv6/ndisc.c                               |    4 -
+ net/ipv6/route.c                               |   59 +++++++++++++++----------
+ net/ipv6/sit.c                                 |    4 -
+ net/sched/sch_teql.c                           |   31 ++++++++-----
+ net/xfrm/xfrm_policy.c                         |    2 
+ 32 files changed, 358 insertions(+), 189 deletions(-)
+
+--- a/drivers/infiniband/core/addr.c
++++ b/drivers/infiniband/core/addr.c
+@@ -215,7 +215,9 @@ static int addr4_resolve(struct sockaddr
+       neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
+       if (!neigh || !(neigh->nud_state & NUD_VALID)) {
+-              neigh_event_send(rt->dst.neighbour, NULL);
++              rcu_read_lock();
++              neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
++              rcu_read_unlock();
+               ret = -ENODATA;
+               if (neigh)
+                       goto release;
+@@ -273,14 +275,16 @@ static int addr6_resolve(struct sockaddr
+               goto put;
+       }
+-      neigh = dst->neighbour;
++      rcu_read_lock();
++      neigh = dst_get_neighbour(dst);
+       if (!neigh || !(neigh->nud_state & NUD_VALID)) {
+-              neigh_event_send(dst->neighbour, NULL);
++              if (neigh)
++                      neigh_event_send(neigh, NULL);
+               ret = -ENODATA;
+-              goto put;
++      } else {
++              ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
+       }
+-
+-      ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
++      rcu_read_unlock();
+ put:
+       dst_release(dst);
+       return ret;
+--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
++++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
+@@ -1328,6 +1328,7 @@ static int pass_accept_req(struct t3cdev
+       struct iwch_ep *child_ep, *parent_ep = ctx;
+       struct cpl_pass_accept_req *req = cplhdr(skb);
+       unsigned int hwtid = GET_TID(req);
++      struct neighbour *neigh;
+       struct dst_entry *dst;
+       struct l2t_entry *l2t;
+       struct rtable *rt;
+@@ -1364,7 +1365,10 @@ static int pass_accept_req(struct t3cdev
+               goto reject;
+       }
+       dst = &rt->dst;
+-      l2t = t3_l2t_get(tdev, dst->neighbour, dst->neighbour->dev);
++      rcu_read_lock();
++      neigh = dst_get_neighbour(dst);
++      l2t = t3_l2t_get(tdev, neigh, neigh->dev);
++      rcu_read_unlock();
+       if (!l2t) {
+               printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
+                      __func__);
+@@ -1874,10 +1878,11 @@ static int is_loopback_dst(struct iw_cm_
+ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
+ {
+-      int err = 0;
+       struct iwch_dev *h = to_iwch_dev(cm_id->device);
++      struct neighbour *neigh;
+       struct iwch_ep *ep;
+       struct rtable *rt;
++      int err = 0;
+       if (is_loopback_dst(cm_id)) {
+               err = -ENOSYS;
+@@ -1933,9 +1938,12 @@ int iwch_connect(struct iw_cm_id *cm_id,
+       }
+       ep->dst = &rt->dst;
++      rcu_read_lock();
++      neigh = dst_get_neighbour(ep->dst);
++
+       /* get a l2t entry */
+-      ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst->neighbour,
+-                           ep->dst->neighbour->dev);
++      ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
++      rcu_read_unlock();
+       if (!ep->l2t) {
+               printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
+               err = -ENOMEM;
+--- a/drivers/infiniband/hw/cxgb4/cm.c
++++ b/drivers/infiniband/hw/cxgb4/cm.c
+@@ -1325,6 +1325,7 @@ static int pass_accept_req(struct c4iw_d
+       unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid));
+       struct tid_info *t = dev->rdev.lldi.tids;
+       unsigned int hwtid = GET_TID(req);
++      struct neighbour *neigh;
+       struct dst_entry *dst;
+       struct l2t_entry *l2t;
+       struct rtable *rt;
+@@ -1357,11 +1358,12 @@ static int pass_accept_req(struct c4iw_d
+               goto reject;
+       }
+       dst = &rt->dst;
+-      if (dst->neighbour->dev->flags & IFF_LOOPBACK) {
++      rcu_read_lock();
++      neigh = dst_get_neighbour(dst);
++      if (neigh->dev->flags & IFF_LOOPBACK) {
+               pdev = ip_dev_find(&init_net, peer_ip);
+               BUG_ON(!pdev);
+-              l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour,
+-                                  pdev, 0);
++              l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, pdev, 0);
+               mtu = pdev->mtu;
+               tx_chan = cxgb4_port_chan(pdev);
+               smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
+@@ -1372,18 +1374,18 @@ static int pass_accept_req(struct c4iw_d
+               rss_qid = dev->rdev.lldi.rxq_ids[cxgb4_port_idx(pdev) * step];
+               dev_put(pdev);
+       } else {
+-              l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, dst->neighbour,
+-                                      dst->neighbour->dev, 0);
++              l2t = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh, neigh->dev, 0);
+               mtu = dst_mtu(dst);
+-              tx_chan = cxgb4_port_chan(dst->neighbour->dev);
+-              smac_idx = (cxgb4_port_viid(dst->neighbour->dev) & 0x7F) << 1;
++              tx_chan = cxgb4_port_chan(neigh->dev);
++              smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
+               step = dev->rdev.lldi.ntxq / dev->rdev.lldi.nchan;
+-              txq_idx = cxgb4_port_idx(dst->neighbour->dev) * step;
+-              ctrlq_idx = cxgb4_port_idx(dst->neighbour->dev);
++              txq_idx = cxgb4_port_idx(neigh->dev) * step;
++              ctrlq_idx = cxgb4_port_idx(neigh->dev);
+               step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan;
+               rss_qid = dev->rdev.lldi.rxq_ids[
+-                        cxgb4_port_idx(dst->neighbour->dev) * step];
++                        cxgb4_port_idx(neigh->dev) * step];
+       }
++      rcu_read_unlock();
+       if (!l2t) {
+               printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
+                      __func__);
+@@ -1847,6 +1849,7 @@ int c4iw_connect(struct iw_cm_id *cm_id,
+       struct c4iw_ep *ep;
+       struct rtable *rt;
+       struct net_device *pdev;
++      struct neighbour *neigh;
+       int step;
+       if ((conn_param->ord > c4iw_max_read_depth) ||
+@@ -1908,14 +1911,16 @@ int c4iw_connect(struct iw_cm_id *cm_id,
+       }
+       ep->dst = &rt->dst;
++      rcu_read_lock();
++      neigh = dst_get_neighbour(ep->dst);
++
+       /* get a l2t entry */
+-      if (ep->dst->neighbour->dev->flags & IFF_LOOPBACK) {
++      if (neigh->dev->flags & IFF_LOOPBACK) {
+               PDBG("%s LOOPBACK\n", __func__);
+               pdev = ip_dev_find(&init_net,
+                                  cm_id->remote_addr.sin_addr.s_addr);
+               ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+-                                      ep->dst->neighbour,
+-                                      pdev, 0);
++                                      neigh, pdev, 0);
+               ep->mtu = pdev->mtu;
+               ep->tx_chan = cxgb4_port_chan(pdev);
+               ep->smac_idx = (cxgb4_port_viid(pdev) & 0x7F) << 1;
+@@ -1930,21 +1935,20 @@ int c4iw_connect(struct iw_cm_id *cm_id,
+               dev_put(pdev);
+       } else {
+               ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t,
+-                                      ep->dst->neighbour,
+-                                      ep->dst->neighbour->dev, 0);
++                                      neigh, neigh->dev, 0);
+               ep->mtu = dst_mtu(ep->dst);
+-              ep->tx_chan = cxgb4_port_chan(ep->dst->neighbour->dev);
+-              ep->smac_idx = (cxgb4_port_viid(ep->dst->neighbour->dev) &
+-                              0x7F) << 1;
++              ep->tx_chan = cxgb4_port_chan(neigh->dev);
++              ep->smac_idx = (cxgb4_port_viid(neigh->dev) & 0x7F) << 1;
+               step = ep->com.dev->rdev.lldi.ntxq /
+                      ep->com.dev->rdev.lldi.nchan;
+-              ep->txq_idx = cxgb4_port_idx(ep->dst->neighbour->dev) * step;
+-              ep->ctrlq_idx = cxgb4_port_idx(ep->dst->neighbour->dev);
++              ep->txq_idx = cxgb4_port_idx(neigh->dev) * step;
++              ep->ctrlq_idx = cxgb4_port_idx(neigh->dev);
+               step = ep->com.dev->rdev.lldi.nrxq /
+                      ep->com.dev->rdev.lldi.nchan;
+               ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
+-                            cxgb4_port_idx(ep->dst->neighbour->dev) * step];
++                            cxgb4_port_idx(neigh->dev) * step];
+       }
++      rcu_read_unlock();
+       if (!ep->l2t) {
+               printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
+               err = -ENOMEM;
+--- a/drivers/infiniband/hw/mlx4/qp.c
++++ b/drivers/infiniband/hw/mlx4/qp.c
+@@ -1301,7 +1301,7 @@ static int build_mlx_header(struct mlx4_
+       int is_eth;
+       int is_vlan = 0;
+       int is_grh;
+-      u16 vlan;
++      u16 vlan = 0;
+       send_size = 0;
+       for (i = 0; i < wr->num_sge; ++i)
+--- a/drivers/infiniband/hw/nes/nes_cm.c
++++ b/drivers/infiniband/hw/nes/nes_cm.c
+@@ -1150,9 +1150,11 @@ static int nes_addr_resolve_neigh(struct
+               neigh_release(neigh);
+       }
+-      if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID)))
+-              neigh_event_send(rt->dst.neighbour, NULL);
+-
++      if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) {
++              rcu_read_lock();
++              neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
++              rcu_read_unlock();
++      }
+       ip_rt_put(rt);
+       return rc;
+ }
+--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
++++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
+@@ -555,14 +555,17 @@ static int path_rec_start(struct net_dev
+       return 0;
+ }
++/* called with rcu_read_lock */
+ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_path *path;
+       struct ipoib_neigh *neigh;
++      struct neighbour *n;
+       unsigned long flags;
+-      neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour, skb->dev);
++      n = dst_get_neighbour(skb_dst(skb));
++      neigh = ipoib_neigh_alloc(n, skb->dev);
+       if (!neigh) {
+               ++dev->stats.tx_dropped;
+               dev_kfree_skb_any(skb);
+@@ -571,9 +574,9 @@ static void neigh_add_path(struct sk_buf
+       spin_lock_irqsave(&priv->lock, flags);
+-      path = __path_find(dev, skb_dst(skb)->neighbour->ha + 4);
++      path = __path_find(dev, n->ha + 4);
+       if (!path) {
+-              path = path_rec_create(dev, skb_dst(skb)->neighbour->ha + 4);
++              path = path_rec_create(dev, n->ha + 4);
+               if (!path)
+                       goto err_path;
+@@ -607,7 +610,7 @@ static void neigh_add_path(struct sk_buf
+                       }
+               } else {
+                       spin_unlock_irqrestore(&priv->lock, flags);
+-                      ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha));
++                      ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha));
+                       return;
+               }
+       } else {
+@@ -634,20 +637,24 @@ err_drop:
+       spin_unlock_irqrestore(&priv->lock, flags);
+ }
++/* called with rcu_read_lock */
+ static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
++      struct dst_entry *dst = skb_dst(skb);
++      struct neighbour *n;
+       /* Look up path record for unicasts */
+-      if (skb_dst(skb)->neighbour->ha[4] != 0xff) {
++      n = dst_get_neighbour(dst);
++      if (n->ha[4] != 0xff) {
+               neigh_add_path(skb, dev);
+               return;
+       }
+       /* Add in the P_Key for multicasts */
+-      skb_dst(skb)->neighbour->ha[8] = (priv->pkey >> 8) & 0xff;
+-      skb_dst(skb)->neighbour->ha[9] = priv->pkey & 0xff;
+-      ipoib_mcast_send(dev, skb_dst(skb)->neighbour->ha + 4, skb);
++      n->ha[8] = (priv->pkey >> 8) & 0xff;
++      n->ha[9] = priv->pkey & 0xff;
++      ipoib_mcast_send(dev, n->ha + 4, skb);
+ }
+ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
+@@ -712,18 +719,23 @@ static int ipoib_start_xmit(struct sk_bu
+ {
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh *neigh;
++      struct neighbour *n = NULL;
+       unsigned long flags;
+-      if (likely(skb_dst(skb) && skb_dst(skb)->neighbour)) {
+-              if (unlikely(!*to_ipoib_neigh(skb_dst(skb)->neighbour))) {
++      rcu_read_lock();
++      if (likely(skb_dst(skb)))
++              n = dst_get_neighbour(skb_dst(skb));
++
++      if (likely(n)) {
++              if (unlikely(!*to_ipoib_neigh(n))) {
+                       ipoib_path_lookup(skb, dev);
+-                      return NETDEV_TX_OK;
++                      goto unlock;
+               }
+-              neigh = *to_ipoib_neigh(skb_dst(skb)->neighbour);
++              neigh = *to_ipoib_neigh(n);
+               if (unlikely((memcmp(&neigh->dgid.raw,
+-                                   skb_dst(skb)->neighbour->ha + 4,
++                                   n->ha + 4,
+                                    sizeof(union ib_gid))) ||
+                            (neigh->dev != dev))) {
+                       spin_lock_irqsave(&priv->lock, flags);
+@@ -740,17 +752,17 @@ static int ipoib_start_xmit(struct sk_bu
+                       ipoib_neigh_free(dev, neigh);
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       ipoib_path_lookup(skb, dev);
+-                      return NETDEV_TX_OK;
++                      goto unlock;
+               }
+               if (ipoib_cm_get(neigh)) {
+                       if (ipoib_cm_up(neigh)) {
+                               ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
+-                              return NETDEV_TX_OK;
++                              goto unlock;
+                       }
+               } else if (neigh->ah) {
+-                      ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(skb_dst(skb)->neighbour->ha));
+-                      return NETDEV_TX_OK;
++                      ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
++                      goto unlock;
+               }
+               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+@@ -784,13 +796,14 @@ static int ipoib_start_xmit(struct sk_bu
+                                          phdr->hwaddr + 4);
+                               dev_kfree_skb_any(skb);
+                               ++dev->stats.tx_dropped;
+-                              return NETDEV_TX_OK;
++                              goto unlock;
+                       }
+                       unicast_arp_send(skb, dev, phdr);
+               }
+       }
+-
++unlock:
++      rcu_read_unlock();
+       return NETDEV_TX_OK;
+ }
+@@ -812,6 +825,8 @@ static int ipoib_hard_header(struct sk_b
+                            const void *daddr, const void *saddr, unsigned len)
+ {
+       struct ipoib_header *header;
++      struct dst_entry *dst;
++      struct neighbour *n;
+       header = (struct ipoib_header *) skb_push(skb, sizeof *header);
+@@ -823,7 +838,11 @@ static int ipoib_hard_header(struct sk_b
+        * destination address onto the front of the skb so we can
+        * figure out where to send the packet later.
+        */
+-      if ((!skb_dst(skb) || !skb_dst(skb)->neighbour) && daddr) {
++      dst = skb_dst(skb);
++      n = NULL;
++      if (dst)
++              n = dst_get_neighbour_raw(dst);
++      if ((!dst || !n) && daddr) {
+               struct ipoib_pseudoheader *phdr =
+                       (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
+               memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
++++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+@@ -258,11 +258,15 @@ static int ipoib_mcast_join_finish(struc
+       netif_tx_lock_bh(dev);
+       while (!skb_queue_empty(&mcast->pkt_queue)) {
+               struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
++              struct dst_entry *dst = skb_dst(skb);
++              struct neighbour *n = NULL;
++
+               netif_tx_unlock_bh(dev);
+               skb->dev = dev;
+-
+-              if (!skb_dst(skb) || !skb_dst(skb)->neighbour) {
++              if (dst)
++                      n = dst_get_neighbour_raw(dst);
++              if (!dst || !n) {
+                       /* put pseudoheader back on for next time */
+                       skb_push(skb, sizeof (struct ipoib_pseudoheader));
+               }
+@@ -715,11 +719,15 @@ void ipoib_mcast_send(struct net_device
+ out:
+       if (mcast && mcast->ah) {
+-              if (skb_dst(skb)                &&
+-                  skb_dst(skb)->neighbour &&
+-                  !*to_ipoib_neigh(skb_dst(skb)->neighbour)) {
+-                      struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb_dst(skb)->neighbour,
+-                                                                      skb->dev);
++              struct dst_entry *dst = skb_dst(skb);
++              struct neighbour *n = NULL;
++
++              rcu_read_lock();
++              if (dst)
++                      n = dst_get_neighbour(dst);
++              if (n && !*to_ipoib_neigh(n)) {
++                      struct ipoib_neigh *neigh = ipoib_neigh_alloc(n,
++                                                                    skb->dev);
+                       if (neigh) {
+                               kref_get(&mcast->ah->ref);
+@@ -727,7 +735,7 @@ out:
+                               list_add_tail(&neigh->list, &mcast->neigh_list);
+                       }
+               }
+-
++              rcu_read_unlock();
+               spin_unlock_irqrestore(&priv->lock, flags);
+               ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
+               return;
+--- a/drivers/net/cxgb3/cxgb3_offload.c
++++ b/drivers/net/cxgb3/cxgb3_offload.c
+@@ -971,7 +971,7 @@ static int nb_callback(struct notifier_b
+       case (NETEVENT_REDIRECT):{
+               struct netevent_redirect *nr = ctx;
+               cxgb_redirect(nr->old, nr->new);
+-              cxgb_neigh_update(nr->new->neighbour);
++              cxgb_neigh_update(dst_get_neighbour(nr->new));
+               break;
+       }
+       default:
+@@ -1116,8 +1116,8 @@ static void cxgb_redirect(struct dst_ent
+       struct l2t_entry *e;
+       struct t3c_tid_entry *te;
+-      olddev = old->neighbour->dev;
+-      newdev = new->neighbour->dev;
++      olddev = dst_get_neighbour(old)->dev;
++      newdev = dst_get_neighbour(new)->dev;
+       if (!is_offloading(olddev))
+               return;
+       if (!is_offloading(newdev)) {
+@@ -1134,7 +1134,7 @@ static void cxgb_redirect(struct dst_ent
+       }
+       /* Add new L2T entry */
+-      e = t3_l2t_get(tdev, new->neighbour, newdev);
++      e = t3_l2t_get(tdev, dst_get_neighbour(new), newdev);
+       if (!e) {
+               printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n",
+                      __func__);
+--- a/drivers/s390/net/qeth_l3_main.c
++++ b/drivers/s390/net/qeth_l3_main.c
+@@ -2742,9 +2742,14 @@ static int qeth_l3_do_ioctl(struct net_d
+ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
+ {
+       int cast_type = RTN_UNSPEC;
++      struct neighbour *n = NULL;
++      struct dst_entry *dst;
+-      if (skb_dst(skb) && skb_dst(skb)->neighbour) {
+-              cast_type = skb_dst(skb)->neighbour->type;
++      dst = skb_dst(skb);
++      if (dst)
++              n = dst_get_neighbour(dst);
++      if (n) {
++              cast_type = n->type;
+               if ((cast_type == RTN_BROADCAST) ||
+                   (cast_type == RTN_MULTICAST) ||
+                   (cast_type == RTN_ANYCAST))
+@@ -2787,6 +2792,9 @@ int inline qeth_l3_get_cast_type(struct
+ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+               struct sk_buff *skb, int ipv, int cast_type)
+ {
++      struct neighbour *n = NULL;
++      struct dst_entry *dst;
++
+       memset(hdr, 0, sizeof(struct qeth_hdr));
+       hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+       hdr->hdr.l3.ext_flags = 0;
+@@ -2804,13 +2812,16 @@ static void qeth_l3_fill_header(struct q
+       }
+       hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
++      dst = skb_dst(skb);
++      if (dst)
++              n = dst_get_neighbour(dst);
+       if (ipv == 4) {
+               /* IPv4 */
+               hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type);
+               memset(hdr->hdr.l3.dest_addr, 0, 12);
+-              if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) {
++              if (n) {
+                       *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
+-                          *((u32 *) skb_dst(skb)->neighbour->primary_key);
++                          *((u32 *) n->primary_key);
+               } else {
+                       /* fill in destination address used in ip header */
+                       *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
+@@ -2821,9 +2832,9 @@ static void qeth_l3_fill_header(struct q
+               hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags6(cast_type);
+               if (card->info.type == QETH_CARD_TYPE_IQD)
+                       hdr->hdr.l3.flags &= ~QETH_HDR_PASSTHRU;
+-              if ((skb_dst(skb)) && (skb_dst(skb)->neighbour)) {
++              if (n) {
+                       memcpy(hdr->hdr.l3.dest_addr,
+-                             skb_dst(skb)->neighbour->primary_key, 16);
++                             n->primary_key, 16);
+               } else {
+                       /* fill in destination address used in ip header */
+                       memcpy(hdr->hdr.l3.dest_addr,
+--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
++++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+@@ -985,7 +985,7 @@ static int init_act_open(struct cxgbi_so
+               csk->saddr.sin_addr.s_addr = chba->ipv4addr;
+       csk->rss_qid = 0;
+-      csk->l2t = t3_l2t_get(t3dev, dst->neighbour, ndev);
++      csk->l2t = t3_l2t_get(t3dev, dst_get_neighbour(dst), ndev);
+       if (!csk->l2t) {
+               pr_err("NO l2t available.\n");
+               return -EINVAL;
+--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
++++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+@@ -1160,7 +1160,7 @@ static int init_act_open(struct cxgbi_so
+       cxgbi_sock_set_flag(csk, CTPF_HAS_ATID);
+       cxgbi_sock_get(csk);
+-      csk->l2t = cxgb4_l2t_get(lldi->l2t, csk->dst->neighbour, ndev, 0);
++      csk->l2t = cxgb4_l2t_get(lldi->l2t, dst_get_neighbour(csk->dst), ndev, 0);
+       if (!csk->l2t) {
+               pr_err("%s, cannot alloc l2t.\n", ndev->name);
+               goto rel_resource;
+--- a/drivers/scsi/cxgbi/libcxgbi.c
++++ b/drivers/scsi/cxgbi/libcxgbi.c
+@@ -492,7 +492,7 @@ static struct cxgbi_sock *cxgbi_check_ro
+               goto err_out;
+       }
+       dst = &rt->dst;
+-      ndev = dst->neighbour->dev;
++      ndev = dst_get_neighbour(dst)->dev;
+       if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
+               pr_info("multi-cast route %pI4, port %u, dev %s.\n",
+@@ -506,7 +506,7 @@ static struct cxgbi_sock *cxgbi_check_ro
+               ndev = ip_dev_find(&init_net, daddr->sin_addr.s_addr);
+               mtu = ndev->mtu;
+               pr_info("rt dev %s, loopback -> %s, mtu %u.\n",
+-                      dst->neighbour->dev->name, ndev->name, mtu);
++                      dst_get_neighbour(dst)->dev->name, ndev->name, mtu);
+       }
+       cdev = cxgbi_device_find_by_netdev(ndev, &port);
+--- a/include/net/arp.h
++++ b/include/net/arp.h
+@@ -16,6 +16,7 @@ extern void     arp_send(int type, int p
+                        const unsigned char *dest_hw,
+                        const unsigned char *src_hw, const unsigned char *th);
+ extern int    arp_bind_neighbour(struct dst_entry *dst);
++extern struct neighbour *__arp_bind_neighbour(struct dst_entry *dst, __be32 nexthop);
+ extern int    arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir);
+ extern void   arp_ifdown(struct net_device *dev);
+--- a/include/net/dst.h
++++ b/include/net/dst.h
+@@ -37,7 +37,7 @@ struct dst_entry {
+       unsigned long           _metrics;
+       unsigned long           expires;
+       struct dst_entry        *path;
+-      struct neighbour        *neighbour;
++      struct neighbour __rcu  *_neighbour;
+       struct hh_cache         *hh;
+ #ifdef CONFIG_XFRM
+       struct xfrm_state       *xfrm;
+@@ -86,6 +86,21 @@ struct dst_entry {
+       };
+ };
++static inline struct neighbour *dst_get_neighbour(struct dst_entry *dst)
++{
++      return rcu_dereference(dst->_neighbour);
++}
++
++static inline struct neighbour *dst_get_neighbour_raw(struct dst_entry *dst)
++{
++      return rcu_dereference_raw(dst->_neighbour);
++}
++
++static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh)
++{
++      rcu_assign_pointer(dst->_neighbour, neigh);
++}
++
+ extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
+ extern const u32 dst_default_metrics[RTAX_MAX];
+@@ -371,8 +386,14 @@ static inline void dst_rcu_free(struct r
+ static inline void dst_confirm(struct dst_entry *dst)
+ {
+-      if (dst)
+-              neigh_confirm(dst->neighbour);
++      if (dst) {
++              struct neighbour *n;
++
++              rcu_read_lock();
++              n = dst_get_neighbour(dst);
++              neigh_confirm(n);
++              rcu_read_unlock();
++      }
+ }
+ static inline void dst_link_failure(struct sk_buff *skb)
+--- a/net/atm/clip.c
++++ b/net/atm/clip.c
+@@ -364,33 +364,37 @@ static netdev_tx_t clip_start_xmit(struc
+                                  struct net_device *dev)
+ {
+       struct clip_priv *clip_priv = PRIV(dev);
++      struct dst_entry *dst = skb_dst(skb);
+       struct atmarp_entry *entry;
++      struct neighbour *n;
+       struct atm_vcc *vcc;
+       int old;
+       unsigned long flags;
+       pr_debug("(skb %p)\n", skb);
+-      if (!skb_dst(skb)) {
++      if (!dst) {
+               pr_err("skb_dst(skb) == NULL\n");
+               dev_kfree_skb(skb);
+               dev->stats.tx_dropped++;
+               return NETDEV_TX_OK;
+       }
+-      if (!skb_dst(skb)->neighbour) {
++      n = dst_get_neighbour(dst);
++      if (!n) {
+ #if 0
+-              skb_dst(skb)->neighbour = clip_find_neighbour(skb_dst(skb), 1);
+-              if (!skb_dst(skb)->neighbour) {
++              n = clip_find_neighbour(skb_dst(skb), 1);
++              if (!n) {
+                       dev_kfree_skb(skb);     /* lost that one */
+                       dev->stats.tx_dropped++;
+                       return 0;
+               }
++              dst_set_neighbour(dst, n);
+ #endif
+               pr_err("NO NEIGHBOUR !\n");
+               dev_kfree_skb(skb);
+               dev->stats.tx_dropped++;
+               return NETDEV_TX_OK;
+       }
+-      entry = NEIGH2ENTRY(skb_dst(skb)->neighbour);
++      entry = NEIGH2ENTRY(n);
+       if (!entry->vccs) {
+               if (time_after(jiffies, entry->expires)) {
+                       /* should be resolved */
+@@ -407,7 +411,7 @@ static netdev_tx_t clip_start_xmit(struc
+       }
+       pr_debug("neigh %p, vccs %p\n", entry, entry->vccs);
+       ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc;
+-      pr_debug("using neighbour %p, vcc %p\n", skb_dst(skb)->neighbour, vcc);
++      pr_debug("using neighbour %p, vcc %p\n", n, vcc);
+       if (entry->vccs->encap) {
+               void *here;
+--- a/net/bridge/br_netfilter.c
++++ b/net/bridge/br_netfilter.c
+@@ -343,24 +343,26 @@ static int br_nf_pre_routing_finish_ipv6
+ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+ {
+       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++      struct neighbour *neigh;
+       struct dst_entry *dst;
+       skb->dev = bridge_parent(skb->dev);
+       if (!skb->dev)
+               goto free_skb;
+       dst = skb_dst(skb);
++      neigh = dst_get_neighbour(dst);
+       if (dst->hh) {
+               neigh_hh_bridge(dst->hh, skb);
+               skb->dev = nf_bridge->physindev;
+               return br_handle_frame_finish(skb);
+-      } else if (dst->neighbour) {
++      } else if (neigh) {
+               /* the neighbour function below overwrites the complete
+                * MAC header, so we save the Ethernet source address and
+                * protocol number. */
+               skb_copy_from_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN);
+               /* tell br_dev_xmit to continue with forwarding */
+               nf_bridge->mask |= BRNF_BRIDGED_DNAT;
+-              return dst->neighbour->output(skb);
++              return neigh->output(skb);
+       }
+ free_skb:
+       kfree_skb(skb);
+--- a/net/core/dst.c
++++ b/net/core/dst.c
+@@ -171,7 +171,7 @@ void *dst_alloc(struct dst_ops *ops, str
+       dst_init_metrics(dst, dst_default_metrics, true);
+       dst->expires = 0UL;
+       dst->path = dst;
+-      dst->neighbour = NULL;
++      RCU_INIT_POINTER(dst->_neighbour, NULL);
+       dst->hh = NULL;
+ #ifdef CONFIG_XFRM
+       dst->xfrm = NULL;
+@@ -231,7 +231,7 @@ struct dst_entry *dst_destroy(struct dst
+       smp_rmb();
+ again:
+-      neigh = dst->neighbour;
++      neigh = rcu_dereference_protected(dst->_neighbour, 1);
+       hh = dst->hh;
+       child = dst->child;
+@@ -240,7 +240,7 @@ again:
+               hh_cache_put(hh);
+       if (neigh) {
+-              dst->neighbour = NULL;
++              RCU_INIT_POINTER(dst->_neighbour, NULL);
+               neigh_release(neigh);
+       }
+@@ -367,14 +367,19 @@ static void dst_ifdown(struct dst_entry
+       if (!unregister) {
+               dst->input = dst->output = dst_discard;
+       } else {
++              struct neighbour *neigh;
++
+               dst->dev = dev_net(dst->dev)->loopback_dev;
+               dev_hold(dst->dev);
+               dev_put(dev);
+-              if (dst->neighbour && dst->neighbour->dev == dev) {
+-                      dst->neighbour->dev = dst->dev;
++              rcu_read_lock();
++              neigh = dst_get_neighbour(dst);
++              if (neigh && neigh->dev == dev) {
++                      neigh->dev = dst->dev;
+                       dev_hold(dst->dev);
+                       dev_put(dev);
+               }
++              rcu_read_unlock();
+       }
+ }
+--- a/net/core/neighbour.c
++++ b/net/core/neighbour.c
+@@ -1173,12 +1173,17 @@ int neigh_update(struct neighbour *neigh
+               while (neigh->nud_state & NUD_VALID &&
+                      (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
+-                      struct neighbour *n1 = neigh;
++                      struct dst_entry *dst = skb_dst(skb);
++                      struct neighbour *n2, *n1 = neigh;
+                       write_unlock_bh(&neigh->lock);
++
++                      rcu_read_lock();
+                       /* On shaper/eql skb->dst->neighbour != neigh :( */
+-                      if (skb_dst(skb) && skb_dst(skb)->neighbour)
+-                              n1 = skb_dst(skb)->neighbour;
++                      if (dst && (n2 = dst_get_neighbour(dst)) != NULL)
++                              n1 = n2;
+                       n1->output(skb);
++                      rcu_read_unlock();
++
+                       write_lock_bh(&neigh->lock);
+               }
+               skb_queue_purge(&neigh->arp_queue);
+@@ -1300,10 +1305,10 @@ EXPORT_SYMBOL(neigh_compat_output);
+ int neigh_resolve_output(struct sk_buff *skb)
+ {
+       struct dst_entry *dst = skb_dst(skb);
+-      struct neighbour *neigh;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       int rc = 0;
+-      if (!dst || !(neigh = dst->neighbour))
++      if (!dst)
+               goto discard;
+       __skb_pull(skb, skb_network_offset(skb));
+@@ -1333,7 +1338,7 @@ out:
+       return rc;
+ discard:
+       NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n",
+-                    dst, dst ? dst->neighbour : NULL);
++                    dst, neigh);
+ out_kfree_skb:
+       rc = -EINVAL;
+       kfree_skb(skb);
+@@ -1347,7 +1352,7 @@ int neigh_connected_output(struct sk_buf
+ {
+       int err;
+       struct dst_entry *dst = skb_dst(skb);
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       struct net_device *dev = neigh->dev;
+       unsigned int seq;
+--- a/net/decnet/dn_neigh.c
++++ b/net/decnet/dn_neigh.c
+@@ -208,7 +208,7 @@ static int dn_neigh_output_packet(struct
+ {
+       struct dst_entry *dst = skb_dst(skb);
+       struct dn_route *rt = (struct dn_route *)dst;
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       struct net_device *dev = neigh->dev;
+       char mac_addr[ETH_ALEN];
+@@ -227,7 +227,7 @@ static int dn_neigh_output_packet(struct
+ static int dn_long_output(struct sk_buff *skb)
+ {
+       struct dst_entry *dst = skb_dst(skb);
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       struct net_device *dev = neigh->dev;
+       int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
+       unsigned char *data;
+@@ -274,7 +274,7 @@ static int dn_long_output(struct sk_buff
+ static int dn_short_output(struct sk_buff *skb)
+ {
+       struct dst_entry *dst = skb_dst(skb);
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       struct net_device *dev = neigh->dev;
+       int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+       struct dn_short_packet *sp;
+@@ -318,7 +318,7 @@ static int dn_short_output(struct sk_buf
+ static int dn_phase3_output(struct sk_buff *skb)
+ {
+       struct dst_entry *dst = skb_dst(skb);
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       struct net_device *dev = neigh->dev;
+       int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+       struct dn_short_packet *sp;
+--- a/net/decnet/dn_route.c
++++ b/net/decnet/dn_route.c
+@@ -241,9 +241,11 @@ static int dn_dst_gc(struct dst_ops *ops
+  */
+ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu)
+ {
++      struct neighbour *n = dst_get_neighbour(dst);
+       u32 min_mtu = 230;
+-      struct dn_dev *dn = dst->neighbour ?
+-                          rcu_dereference_raw(dst->neighbour->dev->dn_ptr) : NULL;
++      struct dn_dev *dn;
++
++      dn = n ? rcu_dereference_raw(n->dev->dn_ptr) : NULL;
+       if (dn && dn->use_long == 0)
+               min_mtu -= 6;
+@@ -715,7 +717,7 @@ static int dn_output(struct sk_buff *skb
+       int err = -EINVAL;
+-      if ((neigh = dst->neighbour) == NULL)
++      if ((neigh = dst_get_neighbour(dst)) == NULL)
+               goto error;
+       skb->dev = dev;
+@@ -750,7 +752,7 @@ static int dn_forward(struct sk_buff *sk
+       struct dst_entry *dst = skb_dst(skb);
+       struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
+       struct dn_route *rt;
+-      struct neighbour *neigh = dst->neighbour;
++      struct neighbour *neigh = dst_get_neighbour(dst);
+       int header_len;
+ #ifdef CONFIG_NETFILTER
+       struct net_device *dev = skb->dev;
+@@ -833,11 +835,11 @@ static int dn_rt_set_next_hop(struct dn_
+       }
+       rt->rt_type = res->type;
+-      if (dev != NULL && rt->dst.neighbour == NULL) {
++      if (dev != NULL && dst_get_neighbour(&rt->dst) == NULL) {
+               n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev);
+               if (IS_ERR(n))
+                       return PTR_ERR(n);
+-              rt->dst.neighbour = n;
++              dst_set_neighbour(&rt->dst, n);
+       }
+       if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu)
+@@ -1144,7 +1146,7 @@ make_route:
+       rt->rt_dst_map    = fld.daddr;
+       rt->rt_src_map    = fld.saddr;
+-      rt->dst.neighbour = neigh;
++      dst_set_neighbour(&rt->dst, neigh);
+       neigh = NULL;
+       rt->dst.lastuse = jiffies;
+@@ -1416,7 +1418,7 @@ make_route:
+       rt->fld.flowidn_iif  = in_dev->ifindex;
+       rt->fld.flowidn_mark = fld.flowidn_mark;
+-      rt->dst.neighbour = neigh;
++      dst_set_neighbour(&rt->dst, neigh);
+       rt->dst.lastuse = jiffies;
+       rt->dst.output = dn_rt_bug;
+       switch(res.type) {
+--- a/net/ipv4/arp.c
++++ b/net/ipv4/arp.c
+@@ -518,26 +518,32 @@ EXPORT_SYMBOL(arp_find);
+ /* END OF OBSOLETE FUNCTIONS */
++struct neighbour *__arp_bind_neighbour(struct dst_entry *dst, __be32 nexthop)
++{
++      struct net_device *dev = dst->dev;
++
++      if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
++              nexthop = 0;
++      return __neigh_lookup_errno(
++#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
++              dev->type == ARPHRD_ATM ?
++              clip_tbl_hook :
++#endif
++              &arp_tbl, &nexthop, dev);
++}
++
+ int arp_bind_neighbour(struct dst_entry *dst)
+ {
+       struct net_device *dev = dst->dev;
+-      struct neighbour *n = dst->neighbour;
++      struct neighbour *n = dst_get_neighbour(dst);
+       if (dev == NULL)
+               return -EINVAL;
+       if (n == NULL) {
+-              __be32 nexthop = ((struct rtable *)dst)->rt_gateway;
+-              if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
+-                      nexthop = 0;
+-              n = __neigh_lookup_errno(
+-#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
+-                                       dev->type == ARPHRD_ATM ?
+-                                       clip_tbl_hook :
+-#endif
+-                                       &arp_tbl, &nexthop, dev);
++              n = __arp_bind_neighbour(dst, ((struct rtable *)dst)->rt_gateway);
+               if (IS_ERR(n))
+                       return PTR_ERR(n);
+-              dst->neighbour = n;
++              dst_set_neighbour(dst, n);
+       }
+       return 0;
+ }
+--- a/net/ipv4/ip_gre.c
++++ b/net/ipv4/ip_gre.c
+@@ -731,9 +731,9 @@ static netdev_tx_t ipgre_tunnel_xmit(str
+               }
+ #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               else if (skb->protocol == htons(ETH_P_IPV6)) {
++                      struct neighbour *neigh = dst_get_neighbour(skb_dst(skb));
+                       const struct in6_addr *addr6;
+                       int addr_type;
+-                      struct neighbour *neigh = skb_dst(skb)->neighbour;
+                       if (neigh == NULL)
+                               goto tx_error;
+--- a/net/ipv4/ip_output.c
++++ b/net/ipv4/ip_output.c
+@@ -182,6 +182,8 @@ static inline int ip_finish_output2(stru
+       struct rtable *rt = (struct rtable *)dst;
+       struct net_device *dev = dst->dev;
+       unsigned int hh_len = LL_RESERVED_SPACE(dev);
++      struct neighbour *neigh;
++      int res;
+       if (rt->rt_type == RTN_MULTICAST) {
+               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
+@@ -202,11 +204,23 @@ static inline int ip_finish_output2(stru
+               kfree_skb(skb);
+               skb = skb2;
+       }
++
++      rcu_read_lock();
++      if (dst->hh) {
++              int res = neigh_hh_output(dst->hh, skb);
+-      if (dst->hh)
+-              return neigh_hh_output(dst->hh, skb);
+-      else if (dst->neighbour)
+-              return dst->neighbour->output(skb);
++              rcu_read_unlock();
++              return res;
++      } else {
++              neigh = dst_get_neighbour(dst);
++              if (neigh) {
++                      res = neigh->output(skb);
++
++                      rcu_read_unlock();
++                      return res;
++              }
++              rcu_read_unlock();
++      }
+       if (net_ratelimit())
+               printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
+--- a/net/ipv4/route.c
++++ b/net/ipv4/route.c
+@@ -416,7 +416,13 @@ static int rt_cache_seq_show(struct seq_
+                          "HHUptod\tSpecDst");
+       else {
+               struct rtable *r = v;
+-              int len;
++              struct neighbour *n;
++              int len, HHUptod;
++
++              rcu_read_lock();
++              n = dst_get_neighbour(&r->dst);
++              HHUptod = (n && (n->nud_state & NUD_CONNECTED)) ? 1 : 0;
++              rcu_read_unlock();
+               seq_printf(seq, "%s\t%08X\t%08X\t%8X\t%d\t%u\t%d\t"
+                             "%08X\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X%n",
+@@ -431,8 +437,7 @@ static int rt_cache_seq_show(struct seq_
+                             dst_metric(&r->dst, RTAX_RTTVAR)),
+                       r->rt_key_tos,
+                       r->dst.hh ? atomic_read(&r->dst.hh->hh_refcnt) : -1,
+-                      r->dst.hh ? (r->dst.hh->hh_output ==
+-                                     dev_queue_xmit) : 0,
++                      HHUptod,
+                       r->rt_spec_dst, &len);
+               seq_printf(seq, "%*s\n", 127 - len, "");
+@@ -1688,23 +1693,25 @@ static int check_peer_redir(struct dst_e
+ {
+       struct rtable *rt = (struct rtable *) dst;
+       __be32 orig_gw = rt->rt_gateway;
++      struct neighbour *n, *old_n;
+       dst_confirm(&rt->dst);
+-      neigh_release(rt->dst.neighbour);
+-      rt->dst.neighbour = NULL;
+-
+       rt->rt_gateway = peer->redirect_learned.a4;
+-      if (arp_bind_neighbour(&rt->dst) ||
+-          !(rt->dst.neighbour->nud_state & NUD_VALID)) {
+-              if (rt->dst.neighbour)
+-                      neigh_event_send(rt->dst.neighbour, NULL);
++      n = __arp_bind_neighbour(&rt->dst, rt->rt_gateway);
++      if (IS_ERR(n))
++              return PTR_ERR(n);
++      old_n = xchg(&rt->dst._neighbour, n);
++      if (old_n)
++              neigh_release(old_n);
++      if (!n || !(n->nud_state & NUD_VALID)) {
++              if (n)
++                      neigh_event_send(n, NULL);
+               rt->rt_gateway = orig_gw;
+               return -EAGAIN;
+       } else {
+               rt->rt_flags |= RTCF_REDIRECTED;
+-              call_netevent_notifiers(NETEVENT_NEIGH_UPDATE,
+-                                      rt->dst.neighbour);
++              call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n);
+       }
+       return 0;
+ }
+--- a/net/ipv6/addrconf.c
++++ b/net/ipv6/addrconf.c
+@@ -656,7 +656,7 @@ ipv6_add_addr(struct inet6_dev *idev, co
+        * layer address of our nexhop router
+        */
+-      if (rt->rt6i_nexthop == NULL)
++      if (dst_get_neighbour_raw(&rt->dst) == NULL)
+               ifa->flags &= ~IFA_F_OPTIMISTIC;
+       ifa->idev = idev;
+--- a/net/ipv6/ip6_fib.c
++++ b/net/ipv6/ip6_fib.c
+@@ -1455,7 +1455,7 @@ static int fib6_age(struct rt6_info *rt,
+                       RT6_TRACE("aging clone %p\n", rt);
+                       return -1;
+               } else if ((rt->rt6i_flags & RTF_GATEWAY) &&
+-                         (!(rt->rt6i_nexthop->flags & NTF_ROUTER))) {
++                         (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) {
+                       RT6_TRACE("purging route %p via non-router but gateway\n",
+                                 rt);
+                       return -1;
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -100,6 +100,8 @@ static int ip6_finish_output2(struct sk_
+ {
+       struct dst_entry *dst = skb_dst(skb);
+       struct net_device *dev = dst->dev;
++      struct neighbour *neigh;
++      int res;
+       skb->protocol = htons(ETH_P_IPV6);
+       skb->dev = dev;
+@@ -134,10 +136,22 @@ static int ip6_finish_output2(struct sk_
+                               skb->len);
+       }
+-      if (dst->hh)
+-              return neigh_hh_output(dst->hh, skb);
+-      else if (dst->neighbour)
+-              return dst->neighbour->output(skb);
++      rcu_read_lock();
++      if (dst->hh) {
++              res = neigh_hh_output(dst->hh, skb);
++
++              rcu_read_unlock();
++              return res;
++      } else {
++              neigh = dst_get_neighbour(dst);
++              if (neigh) {
++                      res = neigh->output(skb);
++
++                      rcu_read_unlock();
++                      return res;
++              }
++              rcu_read_unlock();
++      }
+       IP6_INC_STATS_BH(dev_net(dst->dev),
+                        ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
+@@ -385,6 +399,7 @@ int ip6_forward(struct sk_buff *skb)
+       struct ipv6hdr *hdr = ipv6_hdr(skb);
+       struct inet6_skb_parm *opt = IP6CB(skb);
+       struct net *net = dev_net(dst->dev);
++      struct neighbour *n;
+       u32 mtu;
+       if (net->ipv6.devconf_all->forwarding == 0)
+@@ -459,11 +474,10 @@ int ip6_forward(struct sk_buff *skb)
+          send redirects to source routed frames.
+          We don't send redirects to frames decapsulated from IPsec.
+        */
+-      if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 &&
+-          !skb_sec_path(skb)) {
++      n = dst_get_neighbour(dst);
++      if (skb->dev == dst->dev && n && opt->srcrt == 0 && !skb_sec_path(skb)) {
+               struct in6_addr *target = NULL;
+               struct rt6_info *rt;
+-              struct neighbour *n = dst->neighbour;
+               /*
+                *      incoming and outgoing devices are the same
+@@ -949,8 +963,11 @@ out:
+ static int ip6_dst_lookup_tail(struct sock *sk,
+                              struct dst_entry **dst, struct flowi6 *fl6)
+ {
+-      int err;
+       struct net *net = sock_net(sk);
++#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
++      struct neighbour *n;
++#endif
++      int err;
+       if (*dst == NULL)
+               *dst = ip6_route_output(net, sk, fl6);
+@@ -976,11 +993,14 @@ static int ip6_dst_lookup_tail(struct so
+        * dst entry and replace it instead with the
+        * dst entry of the nexthop router
+        */
+-      if ((*dst)->neighbour && !((*dst)->neighbour->nud_state & NUD_VALID)) {
++      rcu_read_lock();
++      n = dst_get_neighbour(*dst);
++      if (n && !(n->nud_state & NUD_VALID)) {
+               struct inet6_ifaddr *ifp;
+               struct flowi6 fl_gw6;
+               int redirect;
++              rcu_read_unlock();
+               ifp = ipv6_get_ifaddr(net, &fl6->saddr,
+                                     (*dst)->dev, 1);
+@@ -1000,6 +1020,8 @@ static int ip6_dst_lookup_tail(struct so
+                       if ((err = (*dst)->error))
+                               goto out_err_release;
+               }
++      } else {
++              rcu_read_unlock();
+       }
+ #endif
+--- a/net/ipv6/ndisc.c
++++ b/net/ipv6/ndisc.c
+@@ -1244,7 +1244,7 @@ static void ndisc_router_discovery(struc
+       rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
+       if (rt)
+-              neigh = rt->rt6i_nexthop;
++              neigh = dst_get_neighbour(&rt->dst);
+       if (rt && lifetime == 0) {
+               neigh_clone(neigh);
+@@ -1265,7 +1265,7 @@ static void ndisc_router_discovery(struc
+                       return;
+               }
+-              neigh = rt->rt6i_nexthop;
++              neigh = dst_get_neighbour(&rt->dst);
+               if (neigh == NULL) {
+                       ND_PRINTK0(KERN_ERR
+                                  "ICMPv6 RA: %s() got default router without neighbour.\n",
+--- a/net/ipv6/route.c
++++ b/net/ipv6/route.c
+@@ -356,7 +356,7 @@ out:
+ #ifdef CONFIG_IPV6_ROUTER_PREF
+ static void rt6_probe(struct rt6_info *rt)
+ {
+-      struct neighbour *neigh = rt ? rt->rt6i_nexthop : NULL;
++      struct neighbour *neigh;
+       /*
+        * Okay, this does not seem to be appropriate
+        * for now, however, we need to check if it
+@@ -365,8 +365,10 @@ static void rt6_probe(struct rt6_info *r
+        * Router Reachability Probe MUST be rate-limited
+        * to no more than one per minute.
+        */
++      rcu_read_lock();
++      neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
+       if (!neigh || (neigh->nud_state & NUD_VALID))
+-              return;
++              goto out;
+       read_lock_bh(&neigh->lock);
+       if (!(neigh->nud_state & NUD_VALID) &&
+           time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
+@@ -379,8 +381,11 @@ static void rt6_probe(struct rt6_info *r
+               target = (struct in6_addr *)&neigh->primary_key;
+               addrconf_addr_solict_mult(target, &mcaddr);
+               ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
+-      } else
++      } else {
+               read_unlock_bh(&neigh->lock);
++      }
++out:
++      rcu_read_unlock();
+ }
+ #else
+ static inline void rt6_probe(struct rt6_info *rt)
+@@ -404,8 +409,11 @@ static inline int rt6_check_dev(struct r
+ static inline int rt6_check_neigh(struct rt6_info *rt)
+ {
+-      struct neighbour *neigh = rt->rt6i_nexthop;
++      struct neighbour *neigh;
+       int m;
++
++      rcu_read_lock();
++      neigh = dst_get_neighbour(&rt->dst);
+       if (rt->rt6i_flags & RTF_NONEXTHOP ||
+           !(rt->rt6i_flags & RTF_GATEWAY))
+               m = 1;
+@@ -422,6 +430,7 @@ static inline int rt6_check_neigh(struct
+               read_unlock_bh(&neigh->lock);
+       } else
+               m = 0;
++      rcu_read_unlock();
+       return m;
+ }
+@@ -745,8 +754,7 @@ static struct rt6_info *rt6_alloc_cow(st
+                       dst_free(&rt->dst);
+                       return NULL;
+               }
+-              rt->rt6i_nexthop = neigh;
+-
++              dst_set_neighbour(&rt->dst, neigh);
+       }
+       return rt;
+@@ -760,7 +768,7 @@ static struct rt6_info *rt6_alloc_clone(
+               rt->rt6i_dst.plen = 128;
+               rt->rt6i_flags |= RTF_CACHE;
+               rt->dst.flags |= DST_HOST;
+-              rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop);
++              dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
+       }
+       return rt;
+ }
+@@ -794,7 +802,7 @@ restart:
+       dst_hold(&rt->dst);
+       read_unlock_bh(&table->tb6_lock);
+-      if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
++      if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+               nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
+       else if (!(rt->dst.flags & DST_HOST))
+               nrt = rt6_alloc_clone(rt, &fl6->daddr);
+@@ -1058,7 +1066,7 @@ struct dst_entry *icmp6_dst_alloc(struct
+       }
+       rt->rt6i_idev     = idev;
+-      rt->rt6i_nexthop  = neigh;
++      dst_set_neighbour(&rt->dst, neigh);
+       atomic_set(&rt->dst.__refcnt, 1);
+       dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255);
+       rt->dst.output  = ip6_output;
+@@ -1338,12 +1346,12 @@ int ip6_route_add(struct fib6_config *cf
+               rt->rt6i_prefsrc.plen = 0;
+       if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
+-              rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
+-              if (IS_ERR(rt->rt6i_nexthop)) {
+-                      err = PTR_ERR(rt->rt6i_nexthop);
+-                      rt->rt6i_nexthop = NULL;
++              struct neighbour *neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev);
++              if (IS_ERR(neigh)) {
++                      err = PTR_ERR(neigh);
+                       goto out;
+               }
++              dst_set_neighbour(&rt->dst, neigh);
+       }
+       rt->rt6i_flags = cfg->fc_flags;
+@@ -1574,7 +1582,7 @@ void rt6_redirect(const struct in6_addr
+       dst_confirm(&rt->dst);
+       /* Duplicate redirect: silently ignore. */
+-      if (neigh == rt->dst.neighbour)
++      if (neigh == dst_get_neighbour_raw(&rt->dst))
+               goto out;
+       nrt = ip6_rt_copy(rt);
+@@ -1590,7 +1598,7 @@ void rt6_redirect(const struct in6_addr
+       nrt->dst.flags |= DST_HOST;
+       ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
+-      nrt->rt6i_nexthop = neigh_clone(neigh);
++      dst_set_neighbour(&nrt->dst, neigh_clone(neigh));
+       if (ip6_ins_rt(nrt))
+               goto out;
+@@ -1670,7 +1678,7 @@ again:
+          1. It is connected route. Action: COW
+          2. It is gatewayed route or NONEXTHOP route. Action: clone it.
+        */
+-      if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP))
++      if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+               nrt = rt6_alloc_cow(rt, daddr, saddr);
+       else
+               nrt = rt6_alloc_clone(rt, daddr);
+@@ -2035,7 +2043,7 @@ struct rt6_info *addrconf_dst_alloc(stru
+               return ERR_CAST(neigh);
+       }
+-      rt->rt6i_nexthop = neigh;
++      dst_set_neighbour(&rt->dst, neigh);
+       ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+       rt->rt6i_dst.plen = 128;
+@@ -2312,6 +2320,7 @@ static int rt6_fill_node(struct net *net
+       struct nlmsghdr *nlh;
+       long expires;
+       u32 table;
++      struct neighbour *n;
+       if (prefix) {   /* user wants prefix routes only */
+               if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
+@@ -2400,8 +2409,11 @@ static int rt6_fill_node(struct net *net
+       if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+               goto nla_put_failure;
+-      if (rt->dst.neighbour)
+-              NLA_PUT(skb, RTA_GATEWAY, 16, &rt->dst.neighbour->primary_key);
++      rcu_read_lock();
++      n = dst_get_neighbour(&rt->dst);
++      if (n)
++              NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
++      rcu_read_unlock();
+       if (rt->dst.dev)
+               NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
+@@ -2585,6 +2597,7 @@ struct rt6_proc_arg
+ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
+ {
+       struct seq_file *m = p_arg;
++      struct neighbour *n;
+       seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
+@@ -2593,12 +2606,14 @@ static int rt6_info_route(struct rt6_inf
+ #else
+       seq_puts(m, "00000000000000000000000000000000 00 ");
+ #endif
+-
+-      if (rt->rt6i_nexthop) {
+-              seq_printf(m, "%pi6", rt->rt6i_nexthop->primary_key);
++      rcu_read_lock();
++      n = dst_get_neighbour(&rt->dst);
++      if (n) {
++              seq_printf(m, "%pi6", n->primary_key);
+       } else {
+               seq_puts(m, "00000000000000000000000000000000");
+       }
++      rcu_read_unlock();
+       seq_printf(m, " %08x %08x %08x %08x %8s\n",
+                  rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
+                  rt->dst.__use, rt->rt6i_flags,
+--- a/net/ipv6/sit.c
++++ b/net/ipv6/sit.c
+@@ -679,7 +679,7 @@ static netdev_tx_t ipip6_tunnel_xmit(str
+               struct neighbour *neigh = NULL;
+               if (skb_dst(skb))
+-                      neigh = skb_dst(skb)->neighbour;
++                      neigh = dst_get_neighbour(skb_dst(skb));
+               if (neigh == NULL) {
+                       if (net_ratelimit())
+@@ -704,7 +704,7 @@ static netdev_tx_t ipip6_tunnel_xmit(str
+               struct neighbour *neigh = NULL;
+               if (skb_dst(skb))
+-                      neigh = skb_dst(skb)->neighbour;
++                      neigh = dst_get_neighbour(skb_dst(skb));
+               if (neigh == NULL) {
+                       if (net_ratelimit())
+--- a/net/sched/sch_teql.c
++++ b/net/sched/sch_teql.c
+@@ -225,11 +225,11 @@ static int teql_qdisc_init(struct Qdisc
+ static int
+-__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev)
++__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
++             struct net_device *dev, struct netdev_queue *txq,
++             struct neighbour *mn)
+ {
+-      struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0);
+-      struct teql_sched_data *q = qdisc_priv(dev_queue->qdisc);
+-      struct neighbour *mn = skb_dst(skb)->neighbour;
++      struct teql_sched_data *q = qdisc_priv(txq->qdisc);
+       struct neighbour *n = q->ncache;
+       if (mn->tbl == NULL)
+@@ -262,17 +262,26 @@ __teql_resolve(struct sk_buff *skb, stru
+ }
+ static inline int teql_resolve(struct sk_buff *skb,
+-                             struct sk_buff *skb_res, struct net_device *dev)
++                             struct sk_buff *skb_res,
++                             struct net_device *dev,
++                             struct netdev_queue *txq)
+ {
+-      struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
++      struct dst_entry *dst = skb_dst(skb);
++      struct neighbour *mn;
++      int res;
++
+       if (txq->qdisc == &noop_qdisc)
+               return -ENODEV;
+-      if (dev->header_ops == NULL ||
+-          skb_dst(skb) == NULL ||
+-          skb_dst(skb)->neighbour == NULL)
++      if (!dev->header_ops || !dst)
+               return 0;
+-      return __teql_resolve(skb, skb_res, dev);
++
++      rcu_read_lock();
++      mn = dst_get_neighbour(dst);
++      res = mn ? __teql_resolve(skb, skb_res, dev, txq, mn) : 0;
++      rcu_read_unlock();
++
++      return res;
+ }
+ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
+@@ -307,7 +316,7 @@ restart:
+                       continue;
+               }
+-              switch (teql_resolve(skb, skb_res, slave)) {
++              switch (teql_resolve(skb, skb_res, slave, slave_txq)) {
+               case 0:
+                       if (__netif_tx_trylock(slave_txq)) {
+                               unsigned int length = qdisc_pkt_len(skb);
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -1497,7 +1497,7 @@ static struct dst_entry *xfrm_bundle_cre
+               goto free_dst;
+       /* Copy neighbour for reachability confirmation */
+-      dst0->neighbour = neigh_clone(dst->neighbour);
++      dst_set_neighbour(dst0, neigh_clone(dst_get_neighbour(dst)));
+       xfrm_init_path((struct xfrm_dst *)dst0, dst, nfheader_len);
+       xfrm_init_pmtu(dst_prev);
index 21564de207d623939664035dfea542810c39e49d..a379e2de1ec3f6c4e3a535328d315423ee199fcc 100644 (file)
@@ -52,3 +52,4 @@ usb-add-new-zte-3g-dongle-s-pid-to-option.c.patch
 mmc-cb710-core-add-missing-spin_lock_init-for-irq_lock-of-struct-cb710_chip.patch
 powernow-k8-avoid-pstate-msr-accesses-on-systems-supporting-cpb.patch
 powernow-k8-fix-indexing-issue.patch
+0001-net-fix-NULL-dereferences-in-check_peer_redir.patch