diff options
Diffstat (limited to 'net/core/neighbour.c')
| -rw-r--r-- | net/core/neighbour.c | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index d81d026138f0..117afaf51268 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 | { |
| @@ -1199,10 +1201,23 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, | |||
| 1199 | write_unlock_bh(&neigh->lock); | 1201 | write_unlock_bh(&neigh->lock); |
| 1200 | 1202 | ||
| 1201 | rcu_read_lock(); | 1203 | rcu_read_lock(); |
| 1202 | /* On shaper/eql skb->dst->neighbour != neigh :( */ | 1204 | |
| 1203 | if (dst && (n2 = dst_get_neighbour_noref(dst)) != NULL) | 1205 | /* Why not just use 'neigh' as-is? The problem is that |
| 1204 | n1 = n2; | 1206 | * things such as shaper, eql, and sch_teql can end up |
| 1207 | * using alternative, different, neigh objects to output | ||
| 1208 | * the packet in the output path. So what we need to do | ||
| 1209 | * here is re-lookup the top-level neigh in the path so | ||
| 1210 | * we can reinject the packet there. | ||
| 1211 | */ | ||
| 1212 | n2 = NULL; | ||
| 1213 | if (dst) { | ||
| 1214 | n2 = dst_neigh_lookup_skb(dst, skb); | ||
| 1215 | if (n2) | ||
| 1216 | n1 = n2; | ||
| 1217 | } | ||
| 1205 | n1->output(n1, skb); | 1218 | n1->output(n1, skb); |
| 1219 | if (n2) | ||
| 1220 | neigh_release(n2); | ||
| 1206 | rcu_read_unlock(); | 1221 | rcu_read_unlock(); |
| 1207 | 1222 | ||
| 1208 | write_lock_bh(&neigh->lock); | 1223 | write_lock_bh(&neigh->lock); |
