net: split rt_genid for ipv4 and ipv6
authorfan.du <fan.du@windriver.com>
Tue, 30 Jul 2013 00:33:53 +0000 (08:33 +0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 31 Jul 2013 21:56:36 +0000 (14:56 -0700)
Current net name space has only one genid for both IPv4 and IPv6, it has below
drawbacks:

- Add/delete an IPv4 address will invalidate all IPv6 routing table entries.
- Insert/remove XFRM policy will also invalidate both IPv4/IPv6 routing table
  entries even when the policy is only applied for one address family.

Thus, this patch attempt to split one genid for two to cater for IPv4 and IPv6
separately in a fine granularity.

Signed-off-by: Fan Du <fan.du@windriver.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/net_namespace.h
include/net/netns/ipv4.h
include/net/netns/ipv6.h
net/ipv4/route.c
net/ipv6/af_inet6.c
net/ipv6/route.c
net/xfrm/xfrm_policy.c
security/selinux/include/xfrm.h

index 84e37b1ca9e17824ef33ac4fa7708d684f520edc..1313456a0994e03cab0859e7960773b0115f8652 100644 (file)
@@ -119,7 +119,6 @@ struct net {
        struct netns_ipvs       *ipvs;
 #endif
        struct sock             *diag_nlsk;
-       atomic_t                rt_genid;
        atomic_t                fnhe_genid;
 };
 
@@ -333,14 +332,42 @@ static inline void unregister_net_sysctl_table(struct ctl_table_header *header)
 }
 #endif
 
-static inline int rt_genid(struct net *net)
+static inline int rt_genid_ipv4(struct net *net)
 {
-       return atomic_read(&net->rt_genid);
+       return atomic_read(&net->ipv4.rt_genid);
 }
 
-static inline void rt_genid_bump(struct net *net)
+static inline void rt_genid_bump_ipv4(struct net *net)
 {
-       atomic_inc(&net->rt_genid);
+       atomic_inc(&net->ipv4.rt_genid);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static inline int rt_genid_ipv6(struct net *net)
+{
+       return atomic_read(&net->ipv6.rt_genid);
+}
+
+static inline void rt_genid_bump_ipv6(struct net *net)
+{
+       atomic_inc(&net->ipv6.rt_genid);
+}
+#else
+static inline int rt_genid_ipv6(struct net *net)
+{
+       return 0;
+}
+
+static inline void rt_genid_bump_ipv6(struct net *net)
+{
+}
+#endif
+
+/* For callers who don't really care about whether it's IPv4 or IPv6 */
+static inline void rt_genid_bump_all(struct net *net)
+{
+       rt_genid_bump_ipv4(net);
+       rt_genid_bump_ipv6(net);
 }
 
 static inline int fnhe_genid(struct net *net)
index 2ba9de89e8ec778990e8b2ff5183fd3e97eee1aa..bf2ec2202c5698b1bb73e22df60b4a3bc9d95c01 100644 (file)
@@ -77,5 +77,6 @@ struct netns_ipv4 {
        struct fib_rules_ops    *mr_rules_ops;
 #endif
 #endif
+       atomic_t        rt_genid;
 };
 #endif
index 005e2c2e39a9022bad13f4205343263f1821cb22..0fb2401197c51ecf9dd041066975cf5f0bb6a264 100644 (file)
@@ -72,6 +72,7 @@ struct netns_ipv6 {
 #endif
 #endif
        atomic_t                dev_addr_genid;
+       atomic_t                rt_genid;
 };
 
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
index a9a54a2368323243be30f536d45646d48ec6c42d..e805481eff722ec80cfdf2117f93810781c3bd7e 100644 (file)
@@ -435,12 +435,12 @@ static inline int ip_rt_proc_init(void)
 
 static inline bool rt_is_expired(const struct rtable *rth)
 {
-       return rth->rt_genid != rt_genid(dev_net(rth->dst.dev));
+       return rth->rt_genid != rt_genid_ipv4(dev_net(rth->dst.dev));
 }
 
 void rt_cache_flush(struct net *net)
 {
-       rt_genid_bump(net);
+       rt_genid_bump_ipv4(net);
 }
 
 static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
@@ -1458,7 +1458,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 #endif
        rth->dst.output = ip_rt_bug;
 
-       rth->rt_genid   = rt_genid(dev_net(dev));
+       rth->rt_genid   = rt_genid_ipv4(dev_net(dev));
        rth->rt_flags   = RTCF_MULTICAST;
        rth->rt_type    = RTN_MULTICAST;
        rth->rt_is_input= 1;
@@ -1589,7 +1589,7 @@ static int __mkroute_input(struct sk_buff *skb,
                goto cleanup;
        }
 
-       rth->rt_genid = rt_genid(dev_net(rth->dst.dev));
+       rth->rt_genid = rt_genid_ipv4(dev_net(rth->dst.dev));
        rth->rt_flags = flags;
        rth->rt_type = res->type;
        rth->rt_is_input = 1;
@@ -1760,7 +1760,7 @@ local_input:
        rth->dst.tclassid = itag;
 #endif
 
-       rth->rt_genid = rt_genid(net);
+       rth->rt_genid = rt_genid_ipv4(net);
        rth->rt_flags   = flags|RTCF_LOCAL;
        rth->rt_type    = res.type;
        rth->rt_is_input = 1;
@@ -1945,7 +1945,7 @@ add:
 
        rth->dst.output = ip_output;
 
-       rth->rt_genid = rt_genid(dev_net(dev_out));
+       rth->rt_genid = rt_genid_ipv4(dev_net(dev_out));
        rth->rt_flags   = flags;
        rth->rt_type    = type;
        rth->rt_is_input = 0;
@@ -2227,7 +2227,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or
                rt->rt_iif = ort->rt_iif;
                rt->rt_pmtu = ort->rt_pmtu;
 
-               rt->rt_genid = rt_genid(net);
+               rt->rt_genid = rt_genid_ipv4(net);
                rt->rt_flags = ort->rt_flags;
                rt->rt_type = ort->rt_type;
                rt->rt_gateway = ort->rt_gateway;
@@ -2665,7 +2665,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = {
 
 static __net_init int rt_genid_init(struct net *net)
 {
-       atomic_set(&net->rt_genid, 0);
+       atomic_set(&net->ipv4.rt_genid, 0);
        atomic_set(&net->fnhe_genid, 0);
        get_random_bytes(&net->ipv4.dev_addr_genid,
                         sizeof(net->ipv4.dev_addr_genid));
index a5ac969aeefe5337d7fa4c5601912fb96b1a5496..0d1a9b153fbb8c82d680015d382429472d42cfcb 100644 (file)
@@ -766,6 +766,7 @@ static int __net_init inet6_net_init(struct net *net)
 
        net->ipv6.sysctl.bindv6only = 0;
        net->ipv6.sysctl.icmpv6_time = 1*HZ;
+       atomic_set(&net->ipv6.rt_genid, 0);
 
        err = ipv6_init_mibs(net);
        if (err)
index 74ab1f74abcddcb056d9dbe847a1364ab7bceb04..ce9616304521d4e2428fcfe9080bd43aef72771e 100644 (file)
@@ -283,7 +283,7 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
 
                memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
                rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
-               rt->rt6i_genid = rt_genid(net);
+               rt->rt6i_genid = rt_genid_ipv6(net);
                INIT_LIST_HEAD(&rt->rt6i_siblings);
        }
        return rt;
@@ -1061,7 +1061,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
         * DST_OBSOLETE_FORCE_CHK which forces validation calls down
         * into this function always.
         */
-       if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
+       if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev)))
                return NULL;
 
        if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
index e52cab3591dd78c373274bb64420f87383775e8c..d8da6b8c6ba8b980c12564476c06f29d51f21ea0 100644 (file)
@@ -660,7 +660,13 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        xfrm_pol_hold(policy);
        net->xfrm.policy_count[dir]++;
        atomic_inc(&flow_cache_genid);
-       rt_genid_bump(net);
+
+       /* After previous checking, family can either be AF_INET or AF_INET6 */
+       if (policy->family == AF_INET)
+               rt_genid_bump_ipv4(net);
+       else
+               rt_genid_bump_ipv6(net);
+
        if (delpol) {
                xfrm_policy_requeue(delpol, policy);
                __xfrm_policy_unlink(delpol, dir);
index 65f67cb0aefb22f323d8048c140417555c273c9d..6713f04e30ba8810415f88f7ed6e78cb5685f6f4 100644 (file)
@@ -50,8 +50,13 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
 
 static inline void selinux_xfrm_notify_policyload(void)
 {
+       struct net *net;
+
        atomic_inc(&flow_cache_genid);
-       rt_genid_bump(&init_net);
+       rtnl_lock();
+       for_each_net(net)
+               rt_genid_bump_all(net);
+       rtnl_unlock();
 }
 #else
 static inline int selinux_xfrm_enabled(void)