aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/arp.h28
-rw-r--r--include/net/neighbour.h11
-rw-r--r--net/core/neighbour.c12
-rw-r--r--net/ipv4/ip_output.c12
-rw-r--r--net/ipv4/route.c6
5 files changed, 44 insertions, 25 deletions
diff --git a/include/net/arp.h b/include/net/arp.h
index 4a1f3fb562eb..4617d9841132 100644
--- a/include/net/arp.h
+++ b/include/net/arp.h
@@ -15,24 +15,34 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd
15 return val * hash_rnd; 15 return val * hash_rnd;
16} 16}
17 17
18static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) 18static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
19{ 19{
20 struct neigh_hash_table *nht; 20 struct neigh_hash_table *nht = rcu_dereference_bh(arp_tbl.nht);
21 struct neighbour *n; 21 struct neighbour *n;
22 u32 hash_val; 22 u32 hash_val;
23 23
24 rcu_read_lock_bh(); 24 if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
25 nht = rcu_dereference_bh(arp_tbl.nht); 25 key = 0;
26
26 hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); 27 hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift);
27 for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); 28 for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]);
28 n != NULL; 29 n != NULL;
29 n = rcu_dereference_bh(n->next)) { 30 n = rcu_dereference_bh(n->next)) {
30 if (n->dev == dev && *(u32 *)n->primary_key == key) { 31 if (n->dev == dev && *(u32 *)n->primary_key == key)
31 if (!atomic_inc_not_zero(&n->refcnt)) 32 return n;
32 n = NULL;
33 break;
34 }
35 } 33 }
34
35 return NULL;
36}
37
38static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key)
39{
40 struct neighbour *n;
41
42 rcu_read_lock_bh();
43 n = __ipv4_neigh_lookup_noref(dev, key);
44 if (n && !atomic_inc_not_zero(&n->refcnt))
45 n = NULL;
36 rcu_read_unlock_bh(); 46 rcu_read_unlock_bh();
37 47
38 return n; 48 return n;
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 6cdfeedb650b..e1d18bdeebb8 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -202,9 +202,16 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl,
202extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, 202extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl,
203 struct net *net, 203 struct net *net,
204 const void *pkey); 204 const void *pkey);
205extern struct neighbour * neigh_create(struct neigh_table *tbl, 205extern struct neighbour * __neigh_create(struct neigh_table *tbl,
206 const void *pkey,
207 struct net_device *dev,
208 bool want_ref);
209static inline struct neighbour *neigh_create(struct neigh_table *tbl,
206 const void *pkey, 210 const void *pkey,
207 struct net_device *dev); 211 struct net_device *dev)
212{
213 return __neigh_create(tbl, pkey, dev, true);
214}
208extern void neigh_destroy(struct neighbour *neigh); 215extern void neigh_destroy(struct neighbour *neigh);
209extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); 216extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
210extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 217extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index d81d026138f0..a793af9af150 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -474,8 +474,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
474} 474}
475EXPORT_SYMBOL(neigh_lookup_nodev); 475EXPORT_SYMBOL(neigh_lookup_nodev);
476 476
477struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, 477struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
478 struct net_device *dev) 478 struct net_device *dev, bool want_ref)
479{ 479{
480 u32 hash_val; 480 u32 hash_val;
481 int key_len = tbl->key_len; 481 int key_len = tbl->key_len;
@@ -535,14 +535,16 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
535 n1 = rcu_dereference_protected(n1->next, 535 n1 = rcu_dereference_protected(n1->next,
536 lockdep_is_held(&tbl->lock))) { 536 lockdep_is_held(&tbl->lock))) {
537 if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) { 537 if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
538 neigh_hold(n1); 538 if (want_ref)
539 neigh_hold(n1);
539 rc = n1; 540 rc = n1;
540 goto out_tbl_unlock; 541 goto out_tbl_unlock;
541 } 542 }
542 } 543 }
543 544
544 n->dead = 0; 545 n->dead = 0;
545 neigh_hold(n); 546 if (want_ref)
547 neigh_hold(n);
546 rcu_assign_pointer(n->next, 548 rcu_assign_pointer(n->next,
547 rcu_dereference_protected(nht->hash_buckets[hash_val], 549 rcu_dereference_protected(nht->hash_buckets[hash_val],
548 lockdep_is_held(&tbl->lock))); 550 lockdep_is_held(&tbl->lock)));
@@ -558,7 +560,7 @@ out_neigh_release:
558 neigh_release(n); 560 neigh_release(n);
559 goto out; 561 goto out;
560} 562}
561EXPORT_SYMBOL(neigh_create); 563EXPORT_SYMBOL(__neigh_create);
562 564
563static u32 pneigh_hash(const void *pkey, int key_len) 565static u32 pneigh_hash(const void *pkey, int key_len)
564{ 566{
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 2630900e480a..6e9a266a0535 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -170,6 +170,7 @@ static inline int ip_finish_output2(struct sk_buff *skb)
170 struct net_device *dev = dst->dev; 170 struct net_device *dev = dst->dev;
171 unsigned int hh_len = LL_RESERVED_SPACE(dev); 171 unsigned int hh_len = LL_RESERVED_SPACE(dev);
172 struct neighbour *neigh; 172 struct neighbour *neigh;
173 u32 nexthop;
173 174
174 if (rt->rt_type == RTN_MULTICAST) { 175 if (rt->rt_type == RTN_MULTICAST) {
175 IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); 176 IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
@@ -191,15 +192,18 @@ static inline int ip_finish_output2(struct sk_buff *skb)
191 skb = skb2; 192 skb = skb2;
192 } 193 }
193 194
194 rcu_read_lock(); 195 rcu_read_lock_bh();
195 neigh = dst_get_neighbour_noref(dst); 196 nexthop = rt->rt_gateway ? rt->rt_gateway : ip_hdr(skb)->daddr;
197 neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
198 if (unlikely(!neigh))
199 neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
196 if (neigh) { 200 if (neigh) {
197 int res = neigh_output(neigh, skb); 201 int res = neigh_output(neigh, skb);
198 202
199 rcu_read_unlock(); 203 rcu_read_unlock_bh();
200 return res; 204 return res;
201 } 205 }
202 rcu_read_unlock(); 206 rcu_read_unlock_bh();
203 207
204 net_dbg_ratelimited("%s: No header cache and no neighbour!\n", 208 net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
205 __func__); 209 __func__);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6a5afc715558..2f40363e2851 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1098,17 +1098,13 @@ static int slow_chain_length(const struct rtable *head)
1098 1098
1099static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) 1099static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr)
1100{ 1100{
1101 static const __be32 inaddr_any = 0;
1102 struct net_device *dev = dst->dev; 1101 struct net_device *dev = dst->dev;
1103 const __be32 *pkey = daddr; 1102 const __be32 *pkey = daddr;
1104 const struct rtable *rt; 1103 const struct rtable *rt;
1105 struct neighbour *n; 1104 struct neighbour *n;
1106 1105
1107 rt = (const struct rtable *) dst; 1106 rt = (const struct rtable *) dst;
1108 1107 if (rt->rt_gateway)
1109 if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
1110 pkey = &inaddr_any;
1111 else if (rt->rt_gateway)
1112 pkey = (const __be32 *) &rt->rt_gateway; 1108 pkey = (const __be32 *) &rt->rt_gateway;
1113 1109
1114 n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey); 1110 n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey);