return NOTIFY_OK;
 }
 
+static int ipvlan_addr6_validator_event(struct notifier_block *unused,
+                                       unsigned long event, void *ptr)
+{
+       struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr;
+       struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
+       struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+       /* FIXME IPv6 autoconf calls us from bh without RTNL */
+       if (in_softirq())
+               return NOTIFY_DONE;
+
+       if (!netif_is_ipvlan(dev))
+               return NOTIFY_DONE;
+
+       if (!ipvlan || !ipvlan->port)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_UP:
+               if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true))
+                       return notifier_from_errno(-EADDRINUSE);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
 static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
 {
        if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
        return NOTIFY_OK;
 }
 
+static int ipvlan_addr4_validator_event(struct notifier_block *unused,
+                                       unsigned long event, void *ptr)
+{
+       struct in_validator_info *ivi = (struct in_validator_info *)ptr;
+       struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev;
+       struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+       if (!netif_is_ipvlan(dev))
+               return NOTIFY_DONE;
+
+       if (!ipvlan || !ipvlan->port)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_UP:
+               if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false))
+                       return notifier_from_errno(-EADDRINUSE);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
 static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
        .notifier_call = ipvlan_addr4_event,
 };
 
+static struct notifier_block ipvlan_addr4_vtor_notifier_block __read_mostly = {
+       .notifier_call = ipvlan_addr4_validator_event,
+};
+
 static struct notifier_block ipvlan_notifier_block __read_mostly = {
        .notifier_call = ipvlan_device_event,
 };
        .notifier_call = ipvlan_addr6_event,
 };
 
+static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = {
+       .notifier_call = ipvlan_addr6_validator_event,
+};
+
 static void ipvlan_ns_exit(struct net *net)
 {
        struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
        ipvlan_init_secret();
        register_netdevice_notifier(&ipvlan_notifier_block);
        register_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+       register_inet6addr_validator_notifier(
+           &ipvlan_addr6_vtor_notifier_block);
        register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+       register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
 
        err = register_pernet_subsys(&ipvlan_net_ops);
        if (err < 0)
        return 0;
 error:
        unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+       unregister_inetaddr_validator_notifier(
+           &ipvlan_addr4_vtor_notifier_block);
        unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+       unregister_inet6addr_validator_notifier(
+           &ipvlan_addr6_vtor_notifier_block);
        unregister_netdevice_notifier(&ipvlan_notifier_block);
        return err;
 }
        unregister_pernet_subsys(&ipvlan_net_ops);
        unregister_netdevice_notifier(&ipvlan_notifier_block);
        unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
+       unregister_inetaddr_validator_notifier(
+           &ipvlan_addr4_vtor_notifier_block);
        unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
+       unregister_inet6addr_validator_notifier(
+           &ipvlan_addr6_vtor_notifier_block);
 }
 
 module_init(ipvlan_init_module);
 
        unsigned long           ifa_tstamp; /* updated timestamp */
 };
 
+struct in_validator_info {
+       __be32                  ivi_addr;
+       struct in_device        *ivi_dev;
+};
+
 int register_inetaddr_notifier(struct notifier_block *nb);
 int unregister_inetaddr_notifier(struct notifier_block *nb);
+int register_inetaddr_validator_notifier(struct notifier_block *nb);
+int unregister_inetaddr_validator_notifier(struct notifier_block *nb);
 
 void inet_netconf_notify_devconf(struct net *net, int event, int type,
                                 int ifindex, struct ipv4_devconf *devconf);
 
        struct in6_addr         prefix;
 };
 
-
 #include <linux/netdevice.h>
 #include <net/if_inet6.h>
 #include <net/ipv6.h>
 
+struct in6_validator_info {
+       struct in6_addr         i6vi_addr;
+       struct inet6_dev        *i6vi_dev;
+};
+
 #define IN6_ADDR_HSIZE_SHIFT   4
 #define IN6_ADDR_HSIZE         (1 << IN6_ADDR_HSIZE_SHIFT)
 
 int unregister_inet6addr_notifier(struct notifier_block *nb);
 int inet6addr_notifier_call_chain(unsigned long val, void *v);
 
+int register_inet6addr_validator_notifier(struct notifier_block *nb);
+int unregister_inet6addr_validator_notifier(struct notifier_block *nb);
+int inet6addr_validator_notifier_call_chain(unsigned long val, void *v);
+
 void inet6_netconf_notify_devconf(struct net *net, int event, int type,
                                  int ifindex, struct ipv6_devconf *devconf);
 
 
 static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
 
 static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
+static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
 static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
                         int destroy);
 #ifdef CONFIG_SYSCTL
 {
        struct in_device *in_dev = ifa->ifa_dev;
        struct in_ifaddr *ifa1, **ifap, **last_primary;
+       struct in_validator_info ivi;
+       int ret;
 
        ASSERT_RTNL();
 
                }
        }
 
+       /* Allow any devices that wish to register ifaddr validtors to weigh
+        * in now, before changes are committed.  The rntl lock is serializing
+        * access here, so the state should not change between a validator call
+        * and a final notify on commit.  This isn't invoked on promotion under
+        * the assumption that validators are checking the address itself, and
+        * not the flags.
+        */
+       ivi.ivi_addr = ifa->ifa_address;
+       ivi.ivi_dev = ifa->ifa_dev;
+       ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
+                                          NETDEV_UP, &ivi);
+       ret = notifier_to_errno(ret);
+       if (ret) {
+               inet_free_ifa(ifa);
+               return ret;
+       }
+
        if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
                prandom_seed((__force u32) ifa->ifa_local);
                ifap = last_primary;
 }
 EXPORT_SYMBOL(unregister_inetaddr_notifier);
 
+int register_inetaddr_validator_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
+}
+EXPORT_SYMBOL(register_inetaddr_validator_notifier);
+
+int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
+           nb);
+}
+EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
+
 /* Rename ifa_labels for a device name change. Make some effort to preserve
  * existing alias numbering and to create unique labels if possible.
 */
 
        struct net *net = dev_net(idev->dev);
        struct inet6_ifaddr *ifa = NULL;
        struct rt6_info *rt;
+       struct in6_validator_info i6vi;
        unsigned int hash;
        int err = 0;
        int addr_type = ipv6_addr_type(addr);
                return ERR_PTR(-EADDRNOTAVAIL);
 
        rcu_read_lock_bh();
+
+       in6_dev_hold(idev);
+
        if (idev->dead) {
                err = -ENODEV;                  /*XXX*/
                goto out2;
                goto out2;
        }
 
+       i6vi.i6vi_addr = *addr;
+       i6vi.i6vi_dev = idev;
+       rcu_read_unlock_bh();
+
+       err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi);
+
+       rcu_read_lock_bh();
+       err = notifier_to_errno(err);
+       if (err)
+               goto out2;
+
        spin_lock(&addrconf_hash_lock);
 
        /* Ignore adding duplicate addresses on an interface */
        ifa->rt = rt;
 
        ifa->idev = idev;
-       in6_dev_hold(idev);
        /* For caller */
        in6_ifa_hold(ifa);
 
                inet6addr_notifier_call_chain(NETDEV_UP, ifa);
        else {
                kfree(ifa);
+               in6_dev_put(idev);
                ifa = ERR_PTR(err);
        }
 
 
 EXPORT_SYMBOL(__ipv6_addr_type);
 
 static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
+static ATOMIC_NOTIFIER_HEAD(inet6addr_validator_chain);
 
 int register_inet6addr_notifier(struct notifier_block *nb)
 {
 }
 EXPORT_SYMBOL(inet6addr_notifier_call_chain);
 
+int register_inet6addr_validator_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&inet6addr_validator_chain, nb);
+}
+EXPORT_SYMBOL(register_inet6addr_validator_notifier);
+
+int unregister_inet6addr_validator_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&inet6addr_validator_chain, nb);
+}
+EXPORT_SYMBOL(unregister_inet6addr_validator_notifier);
+
+int inet6addr_validator_notifier_call_chain(unsigned long val, void *v)
+{
+       return atomic_notifier_call_chain(&inet6addr_validator_chain, val, v);
+}
+EXPORT_SYMBOL(inet6addr_validator_notifier_call_chain);
+
 static int eafnosupport_ipv6_dst_lookup(struct net *net, struct sock *u1,
                                        struct dst_entry **u2,
                                        struct flowi6 *u3)