|
@@ -1734,45 +1734,97 @@ out:
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
-
|
|
|
/* To make ICMP packets follow the right flow, the multipath hash is
|
|
|
- * calculated from the inner IP addresses in reverse order.
|
|
|
+ * calculated from the inner IP addresses.
|
|
|
*/
|
|
|
-static int ip_multipath_icmp_hash(struct sk_buff *skb)
|
|
|
+static void ip_multipath_l3_keys(const struct sk_buff *skb,
|
|
|
+ struct flow_keys *hash_keys)
|
|
|
{
|
|
|
const struct iphdr *outer_iph = ip_hdr(skb);
|
|
|
- struct icmphdr _icmph;
|
|
|
+ const struct iphdr *inner_iph;
|
|
|
const struct icmphdr *icmph;
|
|
|
struct iphdr _inner_iph;
|
|
|
- const struct iphdr *inner_iph;
|
|
|
+ struct icmphdr _icmph;
|
|
|
+
|
|
|
+ hash_keys->addrs.v4addrs.src = outer_iph->saddr;
|
|
|
+ hash_keys->addrs.v4addrs.dst = outer_iph->daddr;
|
|
|
+ if (likely(outer_iph->protocol != IPPROTO_ICMP))
|
|
|
+ return;
|
|
|
|
|
|
if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0))
|
|
|
- goto standard_hash;
|
|
|
+ return;
|
|
|
|
|
|
icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph),
|
|
|
&_icmph);
|
|
|
if (!icmph)
|
|
|
- goto standard_hash;
|
|
|
+ return;
|
|
|
|
|
|
if (icmph->type != ICMP_DEST_UNREACH &&
|
|
|
icmph->type != ICMP_REDIRECT &&
|
|
|
icmph->type != ICMP_TIME_EXCEEDED &&
|
|
|
- icmph->type != ICMP_PARAMETERPROB) {
|
|
|
- goto standard_hash;
|
|
|
- }
|
|
|
+ icmph->type != ICMP_PARAMETERPROB)
|
|
|
+ return;
|
|
|
|
|
|
inner_iph = skb_header_pointer(skb,
|
|
|
outer_iph->ihl * 4 + sizeof(_icmph),
|
|
|
sizeof(_inner_iph), &_inner_iph);
|
|
|
if (!inner_iph)
|
|
|
- goto standard_hash;
|
|
|
+ return;
|
|
|
+ hash_keys->addrs.v4addrs.src = inner_iph->saddr;
|
|
|
+ hash_keys->addrs.v4addrs.dst = inner_iph->daddr;
|
|
|
+}
|
|
|
|
|
|
- return fib_multipath_hash(inner_iph->daddr, inner_iph->saddr);
|
|
|
+/* if skb is set it will be used and fl4 can be NULL */
|
|
|
+int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
|
|
|
+ const struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct net *net = fi->fib_net;
|
|
|
+ struct flow_keys hash_keys;
|
|
|
+ u32 mhash;
|
|
|
|
|
|
-standard_hash:
|
|
|
- return fib_multipath_hash(outer_iph->saddr, outer_iph->daddr);
|
|
|
-}
|
|
|
+ switch (net->ipv4.sysctl_fib_multipath_hash_policy) {
|
|
|
+ case 0:
|
|
|
+ memset(&hash_keys, 0, sizeof(hash_keys));
|
|
|
+ hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
|
|
|
+ if (skb) {
|
|
|
+ ip_multipath_l3_keys(skb, &hash_keys);
|
|
|
+ } else {
|
|
|
+ hash_keys.addrs.v4addrs.src = fl4->saddr;
|
|
|
+ hash_keys.addrs.v4addrs.dst = fl4->daddr;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ /* skb is currently provided only when forwarding */
|
|
|
+ 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));
|
|
|
+ skb_flow_dissect_flow_keys(skb, &keys, flag);
|
|
|
+ hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
|
|
|
+ hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
|
|
|
+ hash_keys.ports.src = keys.ports.src;
|
|
|
+ hash_keys.ports.dst = keys.ports.dst;
|
|
|
+ hash_keys.basic.ip_proto = keys.basic.ip_proto;
|
|
|
+ } else {
|
|
|
+ memset(&hash_keys, 0, sizeof(hash_keys));
|
|
|
+ hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
|
|
|
+ hash_keys.addrs.v4addrs.src = fl4->saddr;
|
|
|
+ hash_keys.addrs.v4addrs.dst = fl4->daddr;
|
|
|
+ hash_keys.ports.src = fl4->fl4_sport;
|
|
|
+ hash_keys.ports.dst = fl4->fl4_dport;
|
|
|
+ hash_keys.basic.ip_proto = fl4->flowi4_proto;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mhash = flow_hash_from_keys(&hash_keys);
|
|
|
|
|
|
+ return mhash >> 1;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(fib_multipath_hash);
|
|
|
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
|
|
|
|
|
|
static int ip_mkroute_input(struct sk_buff *skb,
|
|
@@ -1782,12 +1834,8 @@ static int ip_mkroute_input(struct sk_buff *skb,
|
|
|
{
|
|
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
if (res->fi && res->fi->fib_nhs > 1) {
|
|
|
- int h;
|
|
|
+ int h = fib_multipath_hash(res->fi, NULL, skb);
|
|
|
|
|
|
- if (unlikely(ip_hdr(skb)->protocol == IPPROTO_ICMP))
|
|
|
- h = ip_multipath_icmp_hash(skb);
|
|
|
- else
|
|
|
- h = fib_multipath_hash(saddr, daddr);
|
|
|
fib_select_multipath(res, h);
|
|
|
}
|
|
|
#endif
|
|
@@ -2203,7 +2251,7 @@ add:
|
|
|
*/
|
|
|
|
|
|
struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
|
|
|
- int mp_hash)
|
|
|
+ const struct sk_buff *skb)
|
|
|
{
|
|
|
struct net_device *dev_out = NULL;
|
|
|
__u8 tos = RT_FL_TOS(fl4);
|
|
@@ -2365,7 +2413,7 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
|
|
|
goto make_route;
|
|
|
}
|
|
|
|
|
|
- fib_select_path(net, &res, fl4, mp_hash);
|
|
|
+ fib_select_path(net, &res, fl4, skb);
|
|
|
|
|
|
dev_out = FIB_RES_DEV(res);
|
|
|
fl4->flowi4_oif = dev_out->ifindex;
|