|
@@ -1214,6 +1214,54 @@ struct dst_entry *ip6_route_input_lookup(struct net *net,
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
|
|
EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
|
|
|
|
|
|
|
|
+static void ip6_multipath_l3_keys(const struct sk_buff *skb,
|
|
|
|
+ struct flow_keys *keys)
|
|
|
|
+{
|
|
|
|
+ const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
|
|
|
|
+ const struct ipv6hdr *key_iph = outer_iph;
|
|
|
|
+ const struct ipv6hdr *inner_iph;
|
|
|
|
+ const struct icmp6hdr *icmph;
|
|
|
|
+ struct ipv6hdr _inner_iph;
|
|
|
|
+
|
|
|
|
+ if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ icmph = icmp6_hdr(skb);
|
|
|
|
+ if (icmph->icmp6_type != ICMPV6_DEST_UNREACH &&
|
|
|
|
+ icmph->icmp6_type != ICMPV6_PKT_TOOBIG &&
|
|
|
|
+ icmph->icmp6_type != ICMPV6_TIME_EXCEED &&
|
|
|
|
+ icmph->icmp6_type != ICMPV6_PARAMPROB)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ inner_iph = skb_header_pointer(skb,
|
|
|
|
+ skb_transport_offset(skb) + sizeof(*icmph),
|
|
|
|
+ sizeof(_inner_iph), &_inner_iph);
|
|
|
|
+ if (!inner_iph)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ key_iph = inner_iph;
|
|
|
|
+out:
|
|
|
|
+ memset(keys, 0, sizeof(*keys));
|
|
|
|
+ keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
|
|
|
|
+ keys->addrs.v6addrs.src = key_iph->saddr;
|
|
|
|
+ keys->addrs.v6addrs.dst = key_iph->daddr;
|
|
|
|
+ keys->tags.flow_label = ip6_flowinfo(key_iph);
|
|
|
|
+ keys->basic.ip_proto = key_iph->nexthdr;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 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 hash_keys;
|
|
|
|
+
|
|
|
|
+ if (skb) {
|
|
|
|
+ ip6_multipath_l3_keys(skb, &hash_keys);
|
|
|
|
+ return flow_hash_from_keys(&hash_keys);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return get_hash_from_flowi6(fl6);
|
|
|
|
+}
|
|
|
|
+
|
|
void ip6_route_input(struct sk_buff *skb)
|
|
void ip6_route_input(struct sk_buff *skb)
|
|
{
|
|
{
|
|
const struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
const struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
@@ -1232,6 +1280,8 @@ void ip6_route_input(struct sk_buff *skb)
|
|
tun_info = skb_tunnel_info(skb);
|
|
tun_info = skb_tunnel_info(skb);
|
|
if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
|
|
if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
|
|
fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
|
|
fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
|
|
|
|
+ if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
|
|
|
|
+ fl6.mp_hash = rt6_multipath_hash(&fl6, skb);
|
|
skb_dst_drop(skb);
|
|
skb_dst_drop(skb);
|
|
skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
|
|
skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
|
|
}
|
|
}
|