Thread (16 messages) 16 messages, 4 authors, 2015-09-29

Re: [PATCH v4 net-next 2/2] ipv4: ICMP packet inspection for multipath

flat view

From: Tom Herbert <hidden>
Date: 2015-09-23 21:01:24

On Wed, Sep 23, 2015 at 12:49 PM, Peter Nørlund [off-list ref] wrote:
quoted hunk ↗ jump to hunk
ICMP packets are inspected to let them route together with the flow they
belong to, minimizing the chance that a problematic path will affect flows
on other paths, and so that anycast environments can work with ECMP.

Signed-off-by: Peter Nørlund <redacted>
---
 include/net/route.h |   11 +++++++++-
 net/ipv4/icmp.c     |   19 ++++++++++++++++-
 net/ipv4/route.c    |   57 +++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/include/net/route.h b/include/net/route.h
index 10a7d21..0f7abdc 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -28,6 +28,7 @@
 #include <net/inetpeer.h>
 #include <net/flow.h>
 #include <net/inet_sock.h>
+#include <net/ip_fib.h>
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
 #include <linux/rcupdate.h>
@@ -112,7 +113,15 @@ struct in_device;
 int ip_rt_init(void);
 void rt_cache_flush(struct net *net);
 void rt_flush_dev(struct net_device *dev);
-struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
+struct rtable *__ip_route_output_key_hash(struct net *, struct flowi4 *flp,
+                                         int mp_hash);
+
+static inline struct rtable *__ip_route_output_key(struct net *net,
+                                                  struct flowi4 *flp)
+{
+       return __ip_route_output_key_hash(net, flp, -1);
+}
+
 struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
                                    struct sock *sk);
 struct dst_entry *ipv4_blackhole_route(struct net *net,
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 79fe05b..f81daca 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -440,6 +440,22 @@ out_unlock:
        icmp_xmit_unlock(sk);
 }

+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+/* Source and destination is swapped. See ip_multipath_icmp_hash */
+static int icmp_multipath_hash_skb(const struct sk_buff *skb)
+{
+       const struct iphdr *iph = ip_hdr(skb);
+
+       return fib_multipath_hash(iph->daddr, iph->saddr);
+}
+
+#else
+
+#define icmp_multipath_hash_skb(skb) (-1)
+
+#endif
+
 static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
@@ -464,7 +480,8 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->flowi4_oif = vrf_master_ifindex(skb_in->dev) ? : skb_in->dev->ifindex;

        security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4));
-       rt = __ip_route_output_key(net, fl4);
+       rt = __ip_route_output_key_hash(net, fl4,
+                                       icmp_multipath_hash_skb(skb_in));
        if (IS_ERR(rt))
                return rt;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 3dc83b6..29048e1 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1652,6 +1652,48 @@ out:
        return err;
 }

+#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.
+ */
+static int ip_multipath_icmp_hash(struct sk_buff *skb)
+{
This seems like overkill to me. Even if you do this on the host no
device in the network is going to crack open ICMP like this for doing
its ECMP. I suppose the argument is that this is another hack needed
to make anycast "work"...
quoted hunk ↗ jump to hunk
+       const struct iphdr *outer_iph = ip_hdr(skb);
+       struct icmphdr _icmph;
+       const struct icmphdr *icmph;
+       struct iphdr _inner_iph;
+       const struct iphdr *inner_iph;
+
+       if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0))
+               goto standard_hash;
+
+       icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph),
+                                  &_icmph);
+       if (!icmph)
+               goto standard_hash;
+
+       if (icmph->type != ICMP_DEST_UNREACH &&
+           icmph->type != ICMP_REDIRECT &&
+           icmph->type != ICMP_TIME_EXCEEDED &&
+           icmph->type != ICMP_PARAMETERPROB) {
+               goto standard_hash;
+       }
+
+       inner_iph = skb_header_pointer(skb,
+                                      outer_iph->ihl * 4 + sizeof(_icmph),
+                                      sizeof(_inner_iph), &_inner_iph);
+       if (!inner_iph)
+               goto standard_hash;
+
+       return fib_multipath_hash(inner_iph->daddr, inner_iph->saddr);
+
+standard_hash:
+       return fib_multipath_hash(outer_iph->saddr, outer_iph->daddr);
+}
+
+#endif /* CONFIG_IP_ROUTE_MULTIPATH */
+
 static int ip_mkroute_input(struct sk_buff *skb,
                            struct fib_result *res,
                            const struct flowi4 *fl4,
@@ -1662,7 +1704,10 @@ static int ip_mkroute_input(struct sk_buff *skb,
        if (res->fi && res->fi->fib_nhs > 1) {
                int h;

-               h = fib_multipath_hash(saddr, daddr);
+               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
@@ -2032,7 +2077,8 @@ add:
  * Major route resolver routine.
  */

-struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
+struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
+                                         int mp_hash)
 {
        struct net_device *dev_out = NULL;
        __u8 tos = RT_FL_TOS(fl4);
@@ -2195,10 +2241,9 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)

 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0) {
-               int h;
-
-               h = fib_multipath_hash(fl4->saddr, fl4->daddr);
-               fib_select_multipath(&res, h);
+               if (mp_hash < 0)
+                       mp_hash = fib_multipath_hash(fl4->saddr, fl4->daddr);
+               fib_select_multipath(&res, mp_hash);
        }
        else
 #endif
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help