ipv6: Implement different admin modes for automatic flow labels
authorTom Herbert <tom@herbertland.com>
Fri, 31 Jul 2015 23:52:12 +0000 (16:52 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 1 Aug 2015 00:07:11 +0000 (17:07 -0700)
Change the meaning of net.ipv6.auto_flowlabels to provide a mode for
automatic flow labels generation. There are four modes:

0: flow labels are disabled
1: flow labels are enabled, sockets can opt-out
2: flow labels are allowed, sockets can opt-in
3: flow labels are enabled and enforced, no opt-out for sockets

np->autoflowlabel is initialized according to the sysctl value.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ip-sysctl.txt
include/net/ipv6.h
net/ipv6/af_inet6.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_tunnel.c
net/ipv6/sysctl_net_ipv6.c

index 00d26d919459422eb0cc0da039e152dd271c8832..9ac3af3ab739180916f0d35e4e9dd13e3879fecb 100644 (file)
@@ -1215,14 +1215,20 @@ flowlabel_consistency - BOOLEAN
        FALSE: disabled
        Default: TRUE
 
-auto_flowlabels - BOOLEAN
-       Automatically generate flow labels based based on a flow hash
-       of the packet. This allows intermediate devices, such as routers,
-       to idenfify packet flows for mechanisms like Equal Cost Multipath
+auto_flowlabels - INTEGER
+       Automatically generate flow labels based on a flow hash of the
+       packet. This allows intermediate devices, such as routers, to
+       identify packet flows for mechanisms like Equal Cost Multipath
        Routing (see RFC 6438).
-       TRUE: enabled
-       FALSE: disabled
-       Default: false
+       0: automatic flow labels are completely disabled
+       1: automatic flow labels are enabled by default, they can be
+          disabled on a per socket basis using the IPV6_AUTOFLOWLABEL
+          socket option
+       2: automatic flow labels are allowed, they may be enabled on a
+          per socket basis using the IPV6_AUTOFLOWLABEL socket option
+       3: automatic flow labels are enabled and enforced, they cannot
+          be disabled by the socket option
+       Default: 0
 
 flowlabel_state_ranges - BOOLEAN
        Split the flow label number space into two ranges. 0-0x7FFFF is
index 3e334b33ef3aecf03d2caba070a3468467f7642f..c02c1c03363a099b90e50df0d0d0840136bcbf85 100644 (file)
@@ -707,36 +707,69 @@ static inline void iph_to_flow_copy_v6addrs(struct flow_keys *flow,
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
+
+/* Sysctl settings for net ipv6.auto_flowlabels */
+#define IP6_AUTO_FLOW_LABEL_OFF                0
+#define IP6_AUTO_FLOW_LABEL_OPTOUT     1
+#define IP6_AUTO_FLOW_LABEL_OPTIN      2
+#define IP6_AUTO_FLOW_LABEL_FORCED     3
+
+#define IP6_AUTO_FLOW_LABEL_MAX                IP6_AUTO_FLOW_LABEL_FORCED
+
+#define IP6_DEFAULT_AUTO_FLOW_LABELS   IP6_AUTO_FLOW_LABEL_OFF
+
 static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
                                        __be32 flowlabel, bool autolabel,
                                        struct flowi6 *fl6)
 {
-       if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) {
-               u32 hash;
+       u32 hash;
 
-               hash = skb_get_hash_flowi6(skb, fl6);
+       if (flowlabel ||
+           net->ipv6.sysctl.auto_flowlabels == IP6_AUTO_FLOW_LABEL_OFF ||
+           (!autolabel &&
+            net->ipv6.sysctl.auto_flowlabels != IP6_AUTO_FLOW_LABEL_FORCED))
+               return flowlabel;
 
-               /* Since this is being sent on the wire obfuscate hash a bit
-                * to minimize possbility that any useful information to an
-                * attacker is leaked. Only lower 20 bits are relevant.
-                */
-               hash ^= hash >> 12;
+       hash = skb_get_hash_flowi6(skb, fl6);
 
-               flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
+       /* Since this is being sent on the wire obfuscate hash a bit
+        * to minimize possbility that any useful information to an
+        * attacker is leaked. Only lower 20 bits are relevant.
+        */
+       rol32(hash, 16);
 
-               if (net->ipv6.sysctl.flowlabel_state_ranges)
-                       flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG;
-       }
+       flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
+
+       if (net->ipv6.sysctl.flowlabel_state_ranges)
+               flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG;
 
        return flowlabel;
 }
+
+static inline int ip6_default_np_autolabel(struct net *net)
+{
+       switch (net->ipv6.sysctl.auto_flowlabels) {
+       case IP6_AUTO_FLOW_LABEL_OFF:
+       case IP6_AUTO_FLOW_LABEL_OPTIN:
+       default:
+               return 0;
+       case IP6_AUTO_FLOW_LABEL_OPTOUT:
+       case IP6_AUTO_FLOW_LABEL_FORCED:
+               return 1;
+       }
+}
 #else
 static inline void ip6_set_txhash(struct sock *sk) { }
 static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
-                                       __be32 flowlabel, bool autolabel)
+                                       __be32 flowlabel, bool autolabel,
+                                       struct flowi6 *fl6)
 {
        return flowlabel;
 }
+static inline int ip6_default_np_autolabel(struct net *net)
+{
+       return 0;
+}
 #endif
 
 
index 7bc92ea4ae8fd17701f65aaa1a3063a3912ba259..3f0ae3a7c0b1ccf2ca67e4cf0b8301bd7e501c54 100644 (file)
@@ -197,6 +197,7 @@ lookup_protocol:
        np->mcast_hops  = IPV6_DEFAULT_MCASTHOPS;
        np->mc_loop     = 1;
        np->pmtudisc    = IPV6_PMTUDISC_WANT;
+       np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk));
        sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
 
        /* Init the ipv4 part of the socket since we can have sockets
@@ -767,7 +768,7 @@ static int __net_init inet6_net_init(struct net *net)
        net->ipv6.sysctl.bindv6only = 0;
        net->ipv6.sysctl.icmpv6_time = 1*HZ;
        net->ipv6.sysctl.flowlabel_consistency = 1;
-       net->ipv6.sysctl.auto_flowlabels = 0;
+       net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS;
        net->ipv6.sysctl.idgen_retries = 3;
        net->ipv6.sysctl.idgen_delay = 1 * HZ;
        net->ipv6.sysctl.flowlabel_state_ranges = 1;
index a7d1ca2337a996debabbe8904f915f4e90105c79..34f121812a1484e0ff23ea337ee39f35424a9628 100644 (file)
@@ -728,7 +728,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
         */
        ipv6h = ipv6_hdr(skb);
        ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
-                    ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6));
+                    ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6));
        ipv6h->hop_limit = tunnel->parms.hop_limit;
        ipv6h->nexthdr = proto;
        ipv6h->saddr = fl6->saddr;
@@ -1182,7 +1182,7 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
 
        ip6_flow_hdr(ipv6h, 0,
                     ip6_make_flowlabel(dev_net(dev), skb,
-                                       t->fl.u.ip6.flowlabel, false,
+                                       t->fl.u.ip6.flowlabel, true,
                                        &t->fl.u.ip6));
        ipv6h->hop_limit = t->parms.hop_limit;
        ipv6h->nexthdr = NEXTHDR_GRE;
index 54e694c4af0ebbd4c065240443020e47d5fb740e..b0ab420612bcc30efd5e58a30bdfb5e730e88b0e 100644 (file)
@@ -1095,7 +1095,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
        skb_reset_network_header(skb);
        ipv6h = ipv6_hdr(skb);
        ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
-                    ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6));
+                    ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6));
        ipv6h->hop_limit = t->parms.hop_limit;
        ipv6h->nexthdr = proto;
        ipv6h->saddr = fl6->saddr;
index db48aebd9c470c2d3a40c7a4250b67887250985e..45243bbe52536d523a7aff0b636a72b8f2e3daf5 100644 (file)
@@ -17,6 +17,9 @@
 #include <net/inet_frag.h>
 
 static int one = 1;
+static int auto_flowlabels_min;
+static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
+
 
 static struct ctl_table ipv6_table_template[] = {
        {
@@ -45,7 +48,9 @@ static struct ctl_table ipv6_table_template[] = {
                .data           = &init_net.ipv6.sysctl.auto_flowlabels,
                .maxlen         = sizeof(int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &auto_flowlabels_min,
+               .extra2         = &auto_flowlabels_max
        },
        {
                .procname       = "fwmark_reflect",