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)