diff options
| author | Tommi Virtanen <tv@debian.org> | 2003-08-18 22:38:29 -0700 |
|---|---|---|
| committer | Stephen Hemminger <shemminger@osdl.org> | 2003-08-18 22:38:29 -0700 |
| commit | ae41b279b70bd2acf43ac682d3124b76e05063e8 (patch) | |
| tree | ca11af3deef8094b1ec0e82df346d67938cb55ae /net | |
| parent | a0ff86b8d39fe78306a605f7ea31b830c831d9a7 (diff) | |
[NET]: Flush hw header caches on NETDEV_CHANGEADDR events.
Diffstat (limited to 'net')
| -rw-r--r-- | net/core/neighbour.c | 28 | ||||
| -rw-r--r-- | net/ipv4/arp.c | 21 | ||||
| -rw-r--r-- | net/ipv6/ndisc.c | 21 |
3 files changed, 70 insertions, 0 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 001fdb40e6de..f87894a7dc93 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -50,6 +50,7 @@ static void neigh_timer_handler(unsigned long arg); static void neigh_app_notify(struct neighbour *n); #endif static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev); +void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); static int neigh_glbl_allocs; static struct neigh_table *neigh_tables; @@ -168,6 +169,33 @@ static void pneigh_queue_purge(struct sk_buff_head *list) } } +void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) +{ + int i; + + write_lock_bh(&tbl->lock); + + for (i=0; i <= NEIGH_HASHMASK; i++) { + struct neighbour *n, **np; + + np = &tbl->hash_buckets[i]; + while ((n = *np) != NULL) { + if (dev && n->dev != dev) { + np = &n->next; + continue; + } + *np = n->next; + write_lock_bh(&n->lock); + n->dead = 1; + neigh_del_timer(n); + write_unlock_bh(&n->lock); + neigh_release(n); + } + } + + write_unlock_bh(&tbl->lock); +} + int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) { int i; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 1324b4e97d83..4fd817944e99 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1071,6 +1071,26 @@ out: return err; } +static int arp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + + switch (event) { + case NETDEV_CHANGEADDR: + neigh_changeaddr(&arp_tbl, dev); + rt_cache_flush(0); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +struct notifier_block arp_netdev_notifier = { + .notifier_call = arp_netdev_event, +}; + /* Note, that it is not on notifier chain. It is necessary, that this routine was called after route cache will be flushed. @@ -1103,6 +1123,7 @@ void __init arp_init(void) neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4"); #endif + register_netdevice_notifier(&arp_netdev_notifier); } #ifdef CONFIG_PROC_FS diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 3728805d5d60..ef434507c04a 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1447,6 +1447,26 @@ int ndisc_rcv(struct sk_buff *skb) return 0; } +static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + + switch (event) { + case NETDEV_CHANGEADDR: + neigh_changeaddr(&nd_tbl, dev); + fib6_run_gc(0); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +struct notifier_block ndisc_netdev_notifier = { + .notifier_call = ndisc_netdev_event, +}; + int __init ndisc_init(struct net_proto_family *ops) { struct ipv6_pinfo *np; @@ -1480,6 +1500,7 @@ int __init ndisc_init(struct net_proto_family *ops) neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6"); #endif + register_netdevice_notifier(&ndisc_netdev_notifier); return 0; } |
