|
|
@@ -245,6 +245,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
|
|
|
#endif
|
|
|
.enhanced_dad = 1,
|
|
|
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
|
|
|
+ .disable_policy = 0,
|
|
|
};
|
|
|
|
|
|
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
|
|
@@ -297,6 +298,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
|
|
|
#endif
|
|
|
.enhanced_dad = 1,
|
|
|
.addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64,
|
|
|
+ .disable_policy = 0,
|
|
|
};
|
|
|
|
|
|
/* Check if a valid qdisc is available */
|
|
|
@@ -944,6 +946,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
|
|
|
const struct in6_addr *peer_addr, int pfxlen,
|
|
|
int scope, u32 flags, u32 valid_lft, u32 prefered_lft)
|
|
|
{
|
|
|
+ struct net *net = dev_net(idev->dev);
|
|
|
struct inet6_ifaddr *ifa = NULL;
|
|
|
struct rt6_info *rt;
|
|
|
unsigned int hash;
|
|
|
@@ -990,6 +993,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ if (net->ipv6.devconf_all->disable_policy ||
|
|
|
+ idev->cnf.disable_policy)
|
|
|
+ rt->dst.flags |= DST_NOPOLICY;
|
|
|
+
|
|
|
neigh_parms_data_state_setall(idev->nd_parms);
|
|
|
|
|
|
ifa->addr = *addr;
|
|
|
@@ -5003,6 +5010,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
|
|
|
#endif
|
|
|
array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad;
|
|
|
array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode;
|
|
|
+ array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
|
|
|
}
|
|
|
|
|
|
static inline size_t inet6_ifla6_size(void)
|
|
|
@@ -5827,6 +5835,105 @@ int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+void addrconf_set_nopolicy(struct rt6_info *rt, int action)
|
|
|
+{
|
|
|
+ if (rt) {
|
|
|
+ if (action)
|
|
|
+ rt->dst.flags |= DST_NOPOLICY;
|
|
|
+ else
|
|
|
+ rt->dst.flags &= ~DST_NOPOLICY;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+void addrconf_disable_policy_idev(struct inet6_dev *idev, int val)
|
|
|
+{
|
|
|
+ struct inet6_ifaddr *ifa;
|
|
|
+
|
|
|
+ read_lock_bh(&idev->lock);
|
|
|
+ list_for_each_entry(ifa, &idev->addr_list, if_list) {
|
|
|
+ spin_lock(&ifa->lock);
|
|
|
+ if (ifa->rt) {
|
|
|
+ struct rt6_info *rt = ifa->rt;
|
|
|
+ struct fib6_table *table = rt->rt6i_table;
|
|
|
+ int cpu;
|
|
|
+
|
|
|
+ read_lock(&table->tb6_lock);
|
|
|
+ addrconf_set_nopolicy(ifa->rt, val);
|
|
|
+ if (rt->rt6i_pcpu) {
|
|
|
+ for_each_possible_cpu(cpu) {
|
|
|
+ struct rt6_info **rtp;
|
|
|
+
|
|
|
+ rtp = per_cpu_ptr(rt->rt6i_pcpu, cpu);
|
|
|
+ addrconf_set_nopolicy(*rtp, val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ read_unlock(&table->tb6_lock);
|
|
|
+ }
|
|
|
+ spin_unlock(&ifa->lock);
|
|
|
+ }
|
|
|
+ read_unlock_bh(&idev->lock);
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+int addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val)
|
|
|
+{
|
|
|
+ struct inet6_dev *idev;
|
|
|
+ struct net *net;
|
|
|
+
|
|
|
+ if (!rtnl_trylock())
|
|
|
+ return restart_syscall();
|
|
|
+
|
|
|
+ *valp = val;
|
|
|
+
|
|
|
+ net = (struct net *)ctl->extra2;
|
|
|
+ if (valp == &net->ipv6.devconf_dflt->disable_policy) {
|
|
|
+ rtnl_unlock();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (valp == &net->ipv6.devconf_all->disable_policy) {
|
|
|
+ struct net_device *dev;
|
|
|
+
|
|
|
+ for_each_netdev(net, dev) {
|
|
|
+ idev = __in6_dev_get(dev);
|
|
|
+ if (idev)
|
|
|
+ addrconf_disable_policy_idev(idev, val);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ idev = (struct inet6_dev *)ctl->extra1;
|
|
|
+ addrconf_disable_policy_idev(idev, val);
|
|
|
+ }
|
|
|
+
|
|
|
+ rtnl_unlock();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static
|
|
|
+int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write,
|
|
|
+ void __user *buffer, size_t *lenp,
|
|
|
+ loff_t *ppos)
|
|
|
+{
|
|
|
+ int *valp = ctl->data;
|
|
|
+ int val = *valp;
|
|
|
+ loff_t pos = *ppos;
|
|
|
+ struct ctl_table lctl;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ lctl = *ctl;
|
|
|
+ lctl.data = &val;
|
|
|
+ ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
|
|
|
+
|
|
|
+ if (write && (*valp != val))
|
|
|
+ ret = addrconf_disable_policy(ctl, valp, val);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ *ppos = pos;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int minus_one = -1;
|
|
|
static const int one = 1;
|
|
|
static const int two_five_five = 255;
|
|
|
@@ -6184,6 +6291,13 @@ static const struct ctl_table addrconf_sysctl[] = {
|
|
|
.mode = 0644,
|
|
|
.proc_handler = addrconf_sysctl_addr_gen_mode,
|
|
|
},
|
|
|
+ {
|
|
|
+ .procname = "disable_policy",
|
|
|
+ .data = &ipv6_devconf.disable_policy,
|
|
|
+ .maxlen = sizeof(int),
|
|
|
+ .mode = 0644,
|
|
|
+ .proc_handler = addrconf_sysctl_disable_policy,
|
|
|
+ },
|
|
|
{
|
|
|
/* sentinel */
|
|
|
}
|