ipv6: Provide ipv6 version of "disable_policy" sysctl
authorDavid Forster <dforster@brocade.com>
Thu, 23 Feb 2017 16:27:18 +0000 (16:27 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 7 Mar 2017 01:10:20 +0000 (17:10 -0800)
This provides equivalent functionality to the existing ipv4
"disable_policy" systcl. ie. Allows IPsec processing to be skipped
on terminating packets on a per-interface basis.

Signed-off-by: David Forster <dforster@brocade.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ipv6.h
include/uapi/linux/ipv6.h
net/ipv6/addrconf.c

index 71be5b330d21305af23f8f7e1779988930755ed8..f0d79bd054cab9eed7a2d63058f25a3a6abd8bbf 100644 (file)
@@ -70,6 +70,7 @@ struct ipv6_devconf {
 #endif
        __u32           enhanced_dad;
        __u32           addr_gen_mode;
+       __s32           disable_policy;
 
        struct ctl_table_header *sysctl_header;
 };
index 8ef9e75e004ebbf951cb50b4b4b17f2c5b907dd4..d8f6a1ac9af49605ff67675d4639bca493da5edb 100644 (file)
@@ -183,6 +183,7 @@ enum {
        DEVCONF_SEG6_REQUIRE_HMAC,
        DEVCONF_ENHANCED_DAD,
        DEVCONF_ADDR_GEN_MODE,
+       DEVCONF_DISABLE_POLICY,
        DEVCONF_MAX
 };
 
index 363172527e433e321cfa9fe8e96cfe32e4a78043..8c69768a5c4606548333842e86b1416a7897ebf5 100644 (file)
@@ -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 */
        }