From patchwork Wed Jun 17 20:08:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Christensen X-Patchwork-Id: 485675 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id F140D140324 for ; Thu, 18 Jun 2015 06:15:01 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ordbogen.com header.i=@ordbogen.com header.b=lKIaOW+p; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752998AbbFQUOz (ORCPT ); Wed, 17 Jun 2015 16:14:55 -0400 Received: from mail.ordbogen.com ([86.58.170.13]:60802 "EHLO mail.ordbogen.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753343AbbFQUOm (ORCPT ); Wed, 17 Jun 2015 16:14:42 -0400 Received: from tyr.webspeed.dk (x1-6-50-3d-e5-df-ec-14.cpe.webspeed.dk [195.41.44.29]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by mail.ordbogen.com (Postfix) with ESMTPSA id E23AEB31B8; Wed, 17 Jun 2015 22:08:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=ordbogen.com; s=20130821; t=1434571707; bh=Kfb8UKmHyF0FnJH08eo/3dCHWooAwEqnZTQN8MEjgnc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lKIaOW+pZ7qfB+1J7h1W82fI2vNLQSl29vrLto0x1t9YFIyU08cE0kaUGSlDmP7kp TbUHXcIW+S8y8OTSqRaVEuRn3j/PUf6MFbuNkhjfy8BfOpXssTcBCp0BkdbR2sk9/G sORlXDoz9hj5JRjwn2rLmhYj1iANyO4Uqo9Uw6cA= From: =?UTF-8?q?Peter=20N=C3=B8rlund?= To: netdev@vger.kernel.org Cc: "David S. Miller" , Alexey Kuznetsov , James Morris , Hideaki YOSHIFUJI , Patrick McHardy , linux-api@vger.kernel.org, =?UTF-8?q?Peter=20N=C3=B8rlund?= Subject: [PATCH net-next 3/3] ipv4: ICMP packet inspection for multipath Date: Wed, 17 Jun 2015 22:08:06 +0200 Message-Id: <1434571686-5149-4-git-send-email-pch@ordbogen.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1434571686-5149-1-git-send-email-pch@ordbogen.com> References: <1434571686-5149-1-git-send-email-pch@ordbogen.com> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org ICMP packets are inspected to let them route together with the flow they belong to, allowing anycast environments to work with ECMP. Signed-off-by: Peter Nørlund --- net/ipv4/icmp.c | 27 ++++++++++++++++++- net/ipv4/route.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 3abcfea..20f1d5e 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -447,6 +447,7 @@ static struct rtable *icmp_route_lookup(struct net *net, { struct rtable *rt, *rt2; struct flowi4 fl4_dec; + struct flowi4 mp_flow; int err; memset(fl4, 0, sizeof(*fl4)); @@ -459,7 +460,31 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->fl4_icmp_type = type; fl4->fl4_icmp_code = code; security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4)); - rt = __ip_route_output_key(net, fl4, NULL); + + /* Source and destination is swapped. See ip_multipath_flow */ + mp_flow.saddr = iph->daddr; + mp_flow.daddr = iph->saddr; + mp_flow.flowi4_proto = iph->protocol; + mp_flow.fl4_sport = 0; + mp_flow.fl4_dport = 0; + if (!ip_is_fragment(iph)) { + if (iph->protocol == IPPROTO_TCP || + iph->protocol == IPPROTO_UDP || + iph->protocol == IPPROTO_SCTP) { + __be16 _ports[2]; + const __be16 *ports; + + ports = skb_header_pointer(skb_in, iph->ihl * 4, + sizeof(_ports), + &_ports); + if (ports) { + mp_flow.fl4_sport = ports[1]; + mp_flow.fl4_dport = ports[0]; + } + } + } + + rt = __ip_route_output_key(net, fl4, &mp_flow); if (IS_ERR(rt)) return rt; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a1ec62c..bab4318 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1635,31 +1635,83 @@ out: /* Fill flow key data based on packet for use in multipath routing. */ static void ip_multipath_flow(const struct sk_buff *skb, struct flowi4 *flow) { - const struct iphdr *iph; - - iph = ip_hdr(skb); - - flow->saddr = iph->saddr; - flow->daddr = iph->daddr; - flow->flowi4_proto = iph->protocol; + struct icmphdr _icmph; + struct iphdr _inner_iph; + const struct iphdr *outer_iph; + const struct icmphdr *icmph; + const struct iphdr *inner_iph; + unsigned int offset; + __be16 _ports[2]; + const __be16 *ports; + + outer_iph = ip_hdr(skb); + + flow->saddr = outer_iph->saddr; + flow->daddr = outer_iph->daddr; + flow->flowi4_proto = outer_iph->protocol; flow->fl4_sport = 0; flow->fl4_dport = 0; - if (unlikely(ip_is_fragment(iph))) + if (unlikely(ip_is_fragment(outer_iph))) return; - if (iph->protocol == IPPROTO_TCP || - iph->protocol == IPPROTO_UDP || - iph->protocol == IPPROTO_SCTP) { - __be16 _ports; - const __be16 *ports; + offset = outer_iph->ihl * 4; - ports = skb_header_pointer(skb, iph->ihl * 4, sizeof(_ports), + if (outer_iph->protocol == IPPROTO_TCP || + outer_iph->protocol == IPPROTO_UDP || + outer_iph->protocol == IPPROTO_SCTP) { + ports = skb_header_pointer(skb, offset, sizeof(_ports), &_ports); if (ports) { flow->fl4_sport = ports[0]; flow->fl4_dport = ports[1]; } + + return; + } + + if (outer_iph->protocol != IPPROTO_ICMP) + return; + + icmph = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); + if (!icmph) + return; + + if (icmph->type != ICMP_DEST_UNREACH && + icmph->type != ICMP_SOURCE_QUENCH && + icmph->type != ICMP_REDIRECT && + icmph->type != ICMP_TIME_EXCEEDED && + icmph->type != ICMP_PARAMETERPROB) { + return; + } + + offset += sizeof(_icmph); + inner_iph = skb_header_pointer(skb, offset, sizeof(_inner_iph), + &_inner_iph); + if (inner_iph) + return; + + /* Since the ICMP payload contains a packet sent from the current + * recipient, we swap source and destination addresses and ports + */ + flow->saddr = inner_iph->daddr; + flow->daddr = inner_iph->saddr; + flow->flowi4_proto = inner_iph->protocol; + + if (unlikely(ip_is_fragment(inner_iph))) + return; + + if (inner_iph->protocol != IPPROTO_TCP && + inner_iph->protocol != IPPROTO_UDP && + inner_iph->protocol != IPPROTO_SCTP) { + return; + } + + offset += inner_iph->ihl * 4; + ports = skb_header_pointer(skb, offset, sizeof(_ports), &_ports); + if (ports) { + flow->fl4_sport = ports[1]; + flow->fl4_dport = ports[0]; } } #endif /* CONFIG_IP_ROUTE_MULTIPATH */