diff options
-rw-r--r-- | include/net/arp.h | 28 | ||||
-rw-r--r-- | include/net/neighbour.h | 11 | ||||
-rw-r--r-- | net/core/neighbour.c | 12 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 12 | ||||
-rw-r--r-- | net/ipv4/route.c | 6 |
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 | ||
18 | static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) | 18 | static 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 | |||
38 | static 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, | |||
202 | extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, | 202 | extern 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); |
205 | extern struct neighbour * neigh_create(struct neigh_table *tbl, | 205 | extern struct neighbour * __neigh_create(struct neigh_table *tbl, |
206 | const void *pkey, | ||
207 | struct net_device *dev, | ||
208 | bool want_ref); | ||
209 | static 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 | } | ||
208 | extern void neigh_destroy(struct neighbour *neigh); | 215 | extern void neigh_destroy(struct neighbour *neigh); |
209 | extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); | 216 | extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); |
210 | extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, | 217 | extern 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 | } |
475 | EXPORT_SYMBOL(neigh_lookup_nodev); | 475 | EXPORT_SYMBOL(neigh_lookup_nodev); |
476 | 476 | ||
477 | struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, | 477 | struct 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 | } |
561 | EXPORT_SYMBOL(neigh_create); | 563 | EXPORT_SYMBOL(__neigh_create); |
562 | 564 | ||
563 | static u32 pneigh_hash(const void *pkey, int key_len) | 565 | static 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 | ||
1099 | static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) | 1099 | static 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); |