--- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -92,6 +92,12 @@ extern void addrconf_join_solict(struc extern void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); +extern int (*ipv6_dev_get_saddr_hook)(struct net *net, + const struct net_device *dev, + const struct in6_addr *daddr, + unsigned int prefs, + struct in6_addr *saddr); + static inline unsigned long addrconf_timeout_fixup(u32 timeout, unsigned int unit) { --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -6,7 +6,6 @@ config BRIDGE tristate "802.1d Ethernet Bridging" select LLC select STP - depends on IPV6 || IPV6=n ---help--- If you say Y here, then your Linux box will be able to act as an Ethernet bridge, which means that the different Ethernet segments it --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -44,3 +44,4 @@ obj-y += addrconf_core.o exthdrs_core.o obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o +obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_stubs.o --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1243,7 +1243,7 @@ out: return ret; } -int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, +static int __ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, const struct in6_addr *daddr, unsigned int prefs, struct in6_addr *saddr) { @@ -1368,7 +1368,6 @@ try_nextdev: in6_ifa_put(hiscore->ifa); return 0; } -EXPORT_SYMBOL(ipv6_dev_get_saddr); int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, unsigned char banned_flags) @@ -4968,6 +4967,9 @@ int __init addrconf_init(void) ipv6_addr_label_rtnl_register(); + BUG_ON(ipv6_dev_get_saddr_hook != NULL); + rcu_assign_pointer(ipv6_dev_get_saddr_hook, __ipv6_dev_get_saddr); + return 0; errout: rtnl_af_unregister(&inet6_ops); @@ -4986,6 +4988,9 @@ void addrconf_cleanup(void) struct net_device *dev; int i; + rcu_assign_pointer(ipv6_dev_get_saddr_hook, NULL); + synchronize_rcu(); + unregister_netdevice_notifier(&ipv6_dev_notf); unregister_pernet_subsys(&addrconf_ops); ipv6_addr_label_cleanup(); --- /dev/null +++ b/net/ipv6/inet6_stubs.c @@ -0,0 +1,33 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/export.h> +#include <net/ipv6.h> + +int (*ipv6_dev_get_saddr_hook)(struct net *net, const struct net_device *dev, + const struct in6_addr *daddr, unsigned int prefs, + struct in6_addr *saddr); + +EXPORT_SYMBOL(ipv6_dev_get_saddr_hook); + +int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, + const struct in6_addr *daddr, unsigned int prefs, + struct in6_addr *saddr) +{ + int ret = -EADDRNOTAVAIL; + typeof(ipv6_dev_get_saddr_hook) dev_get_saddr; + + rcu_read_lock(); + dev_get_saddr = rcu_dereference(ipv6_dev_get_saddr_hook); + + if (dev_get_saddr) + ret = dev_get_saddr(net, dst_dev, daddr, prefs, saddr); + + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL(ipv6_dev_get_saddr); +