return false;
 }
 
-static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
+static struct rt6_info *rt6_multipath_select(const struct net *net,
+                                            struct rt6_info *match,
                                             struct flowi6 *fl6, int oif,
                                             const struct sk_buff *skb,
                                             int strict)
         * case it will always be non-zero. Otherwise now is the time to do it.
         */
        if (!fl6->mp_hash)
-               fl6->mp_hash = rt6_multipath_hash(fl6, skb, NULL);
+               fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL);
 
        if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
                return match;
                rt = rt6_device_match(net, rt, &fl6->saddr,
                                      fl6->flowi6_oif, flags);
                if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
-                       rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif,
+                       rt = rt6_multipath_select(net, rt, fl6, fl6->flowi6_oif,
                                                  skb, flags);
        }
        if (rt == net->ipv6.ip6_null_entry) {
 redo_rt6_select:
        rt = rt6_select(net, fn, oif, strict);
        if (rt->rt6i_nsiblings)
-               rt = rt6_multipath_select(rt, fl6, oif, skb, strict);
+               rt = rt6_multipath_select(net, rt, fl6, oif, skb, strict);
        if (rt == net->ipv6.ip6_null_entry) {
                fn = fib6_backtrack(fn, &fl6->saddr);
                if (fn)
 }
 
 /* if skb is set it will be used and fl6 can be NULL */
-u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
-                      struct flow_keys *flkeys)
+u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
+                      const struct sk_buff *skb, struct flow_keys *flkeys)
 {
        struct flow_keys hash_keys;
        u32 mhash;
 
-       memset(&hash_keys, 0, sizeof(hash_keys));
-       hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
-       if (skb) {
-               ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
-       } else {
-               hash_keys.addrs.v6addrs.src = fl6->saddr;
-               hash_keys.addrs.v6addrs.dst = fl6->daddr;
-               hash_keys.tags.flow_label = (__force u32)fl6->flowlabel;
-               hash_keys.basic.ip_proto = fl6->flowi6_proto;
+       switch (net->ipv6.sysctl.multipath_hash_policy) {
+       case 0:
+               memset(&hash_keys, 0, sizeof(hash_keys));
+               hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+               if (skb) {
+                       ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
+               } else {
+                       hash_keys.addrs.v6addrs.src = fl6->saddr;
+                       hash_keys.addrs.v6addrs.dst = fl6->daddr;
+                       hash_keys.tags.flow_label = (__force u32)fl6->flowlabel;
+                       hash_keys.basic.ip_proto = fl6->flowi6_proto;
+               }
+               break;
+       case 1:
+               if (skb) {
+                       unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
+                       struct flow_keys keys;
+
+                       /* short-circuit if we already have L4 hash present */
+                       if (skb->l4_hash)
+                               return skb_get_hash_raw(skb) >> 1;
+
+                       memset(&hash_keys, 0, sizeof(hash_keys));
+
+                        if (!flkeys) {
+                               skb_flow_dissect_flow_keys(skb, &keys, flag);
+                               flkeys = &keys;
+                       }
+                       hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+                       hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src;
+                       hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst;
+                       hash_keys.ports.src = flkeys->ports.src;
+                       hash_keys.ports.dst = flkeys->ports.dst;
+                       hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
+               } else {
+                       memset(&hash_keys, 0, sizeof(hash_keys));
+                       hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+                       hash_keys.addrs.v6addrs.src = fl6->saddr;
+                       hash_keys.addrs.v6addrs.dst = fl6->daddr;
+                       hash_keys.ports.src = fl6->fl6_sport;
+                       hash_keys.ports.dst = fl6->fl6_dport;
+                       hash_keys.basic.ip_proto = fl6->flowi6_proto;
+               }
+               break;
        }
        mhash = flow_hash_from_keys(&hash_keys);
 
                flkeys = &_flkeys;
 
        if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
-               fl6.mp_hash = rt6_multipath_hash(&fl6, skb, flkeys);
+               fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys);
        skb_dst_drop(skb);
        skb_dst_set(skb,
                    ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags));
 
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 #include <net/inet_frag.h>
+#include <net/netevent.h>
 #ifdef CONFIG_NETLABEL
 #include <net/calipso.h>
 #endif
 
+static int zero;
 static int one = 1;
 static int auto_flowlabels_min;
 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
 
+static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,
+                                         void __user *buffer, size_t *lenp,
+                                         loff_t *ppos)
+{
+       struct net *net;
+       int ret;
+
+       net = container_of(table->data, struct net,
+                          ipv6.sysctl.multipath_hash_policy);
+       ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       if (write && ret == 0)
+               call_netevent_notifiers(NETEVENT_IPV6_MPATH_HASH_UPDATE, net);
+
+       return ret;
+}
 
 static struct ctl_table ipv6_table_template[] = {
        {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "fib_multipath_hash_policy",
+               .data           = &init_net.ipv6.sysctl.multipath_hash_policy,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_rt6_multipath_hash_policy,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
        { }
 };
 
        ipv6_table[11].data = &net->ipv6.sysctl.max_hbh_opts_cnt;
        ipv6_table[12].data = &net->ipv6.sysctl.max_dst_opts_len;
        ipv6_table[13].data = &net->ipv6.sysctl.max_hbh_opts_len;
+       ipv6_table[14].data = &net->ipv6.sysctl.multipath_hash_policy,
 
        ipv6_route_table = ipv6_route_sysctl_init(net);
        if (!ipv6_route_table)