summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorTommi Virtanen <tv@debian.org>2003-08-18 22:38:29 -0700
committerStephen Hemminger <shemminger@osdl.org>2003-08-18 22:38:29 -0700
commitae41b279b70bd2acf43ac682d3124b76e05063e8 (patch)
treeca11af3deef8094b1ec0e82df346d67938cb55ae /net
parenta0ff86b8d39fe78306a605f7ea31b830c831d9a7 (diff)
[NET]: Flush hw header caches on NETDEV_CHANGEADDR events.
Diffstat (limited to 'net')
-rw-r--r--net/core/neighbour.c28
-rw-r--r--net/ipv4/arp.c21
-rw-r--r--net/ipv6/ndisc.c21
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;
}