From patchwork Thu Nov 29 18:35:46 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Gross X-Patchwork-Id: 202810 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 1AC992C0087 for ; Fri, 30 Nov 2012 05:36:18 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754568Ab2K2SgF (ORCPT ); Thu, 29 Nov 2012 13:36:05 -0500 Received: from na3sys009aog111.obsmtp.com ([74.125.149.205]:48545 "HELO na3sys009aog111.obsmtp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1752689Ab2K2SgD (ORCPT ); Thu, 29 Nov 2012 13:36:03 -0500 Received: from mail-pa0-f70.google.com ([209.85.220.70]) (using TLSv1) by na3sys009aob111.postini.com ([74.125.148.12]) with SMTP ID DSNKULerEo5os5Vy2QGQgdltbO/shOSiLwd/@postini.com; Thu, 29 Nov 2012 10:36:02 PST Received: by mail-pa0-f70.google.com with SMTP id hz11so15482417pad.1 for ; Thu, 29 Nov 2012 10:36:02 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=kS0MqyqLLjBq3U5SWTcaLfeAys/og9QKL9a2fe2W8x8=; b=Y71lbi6+idp2oT4OrmVfqCcdcT1lTjBJ0VeVgauWuintaaN+RXbNwh7aO6goAPO+KI 55qQrX0+0aQR6yIaJEKeosTr+xQT3AnjcPlb+wDStkYr/ZsQ1jTJtMUrw3mEGXrlSRYd znitBdem2lYuOHfIYM72ewUt/eoUH0q6tZVaMAqiO7Py40cn87raoboQnW+IEZqUAzFE FvP7hu03Voc++uH7OT3rMeeCGvd5S+afGwsAozOQXuXr5RF9R9ogdNY+3FjAq9mBZ3xp VkumVQNflq7oo835ZN6GOe0/fVWMC5gd92dq/GZ0L+uZ6rWgPZw73uFmOtH/yxg5rjIV rN1Q== Received: by 10.66.73.225 with SMTP id o1mr64287025pav.70.1354214161994; Thu, 29 Nov 2012 10:36:01 -0800 (PST) Received: by 10.66.73.225 with SMTP id o1mr64287011pav.70.1354214161893; Thu, 29 Nov 2012 10:36:01 -0800 (PST) Received: from ubuntu.localdomain (c-24-7-88-182.hsd1.ca.comcast.net. [24.7.88.182]) by mx.google.com with ESMTPS id j9sm1455776paw.2.2012.11.29.10.36.00 (version=SSLv3 cipher=OTHER); Thu, 29 Nov 2012 10:36:01 -0800 (PST) From: Jesse Gross To: David Miller Cc: netdev@vger.kernel.org, dev@openvswitch.org, Ansis Atteka Subject: [PATCH net-next 4/7] openvswitch: add ipv6 'set' action Date: Thu, 29 Nov 2012 10:35:46 -0800 Message-Id: <1354214149-33651-5-git-send-email-jesse@nicira.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1354214149-33651-1-git-send-email-jesse@nicira.com> References: <1354214149-33651-1-git-send-email-jesse@nicira.com> X-Gm-Message-State: ALoCoQmnuGtFq21W29tICoTZWbPdMHZpn7RLeXAhy8UqDNTL2nuTLSEkdzqtnIyGTcnqRSd2I4RF1qFOBWR0sdrlsgoxQiVv07LcSLZSBM3w8MD532nqjSG7idQSdEfuowpZnQg2jiVJo+M3Q7bZpA6HeqcDGK7TY2KaVatwVUlwSI2Hu2TTXmM= Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Ansis Atteka This patch adds ipv6 set action functionality. It allows to change traffic class, flow label, hop-limit, ipv6 source and destination address fields. Signed-off-by: Ansis Atteka Signed-off-by: Jesse Gross --- net/openvswitch/actions.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ net/openvswitch/datapath.c | 20 ++++++++++ 2 files changed, 113 insertions(+) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 0811447..a58ed27 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, *addr = new_addr; } +static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4]) +{ + int transport_len = skb->len - skb_transport_offset(skb); + + if (l4_proto == IPPROTO_TCP) { + if (likely(transport_len >= sizeof(struct tcphdr))) + inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, + addr, new_addr, 1); + } else if (l4_proto == IPPROTO_UDP) { + if (likely(transport_len >= sizeof(struct udphdr))) { + struct udphdr *uh = udp_hdr(skb); + + if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + inet_proto_csum_replace16(&uh->check, skb, + addr, new_addr, 1); + if (!uh->check) + uh->check = CSUM_MANGLED_0; + } + } + } +} + +static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4], + bool recalculate_csum) +{ + if (recalculate_csum) + update_ipv6_checksum(skb, l4_proto, addr, new_addr); + + skb->rxhash = 0; + memcpy(addr, new_addr, sizeof(__be32[4])); +} + +static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) +{ + nh->priority = tc >> 4; + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4); +} + +static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl) +{ + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16; + nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8; + nh->flow_lbl[2] = fl & 0x000000FF; +} + static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) { csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); @@ -195,6 +243,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) return 0; } +static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) +{ + struct ipv6hdr *nh; + int err; + __be32 *saddr; + __be32 *daddr; + + err = make_writable(skb, skb_network_offset(skb) + + sizeof(struct ipv6hdr)); + if (unlikely(err)) + return err; + + nh = ipv6_hdr(skb); + saddr = (__be32 *)&nh->saddr; + daddr = (__be32 *)&nh->daddr; + + if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) + set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr, + ipv6_key->ipv6_src, true); + + if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) { + unsigned int offset = 0; + int flags = IP6_FH_F_SKIP_RH; + bool recalc_csum = true; + + if (ipv6_ext_hdr(nh->nexthdr)) + recalc_csum = ipv6_find_hdr(skb, &offset, + NEXTHDR_ROUTING, NULL, + &flags) != NEXTHDR_ROUTING; + + set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr, + ipv6_key->ipv6_dst, recalc_csum); + } + + set_ipv6_tc(nh, ipv6_key->ipv6_tclass); + set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label)); + nh->hop_limit = ipv6_key->ipv6_hlimit; + + return 0; +} + /* Must follow make_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) @@ -347,6 +436,10 @@ static int execute_set_action(struct sk_buff *skb, err = set_ipv4(skb, nla_data(nested_attr)); break; + case OVS_KEY_ATTR_IPV6: + err = set_ipv6(skb, nla_data(nested_attr)); + break; + case OVS_KEY_ATTR_TCP: err = set_tcp(skb, nla_data(nested_attr)); break; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4c4b62c..fd4a6a4 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -479,6 +479,7 @@ static int validate_set(const struct nlattr *a, switch (key_type) { const struct ovs_key_ipv4 *ipv4_key; + const struct ovs_key_ipv6 *ipv6_key; case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_ETHERNET: @@ -500,6 +501,25 @@ static int validate_set(const struct nlattr *a, break; + case OVS_KEY_ATTR_IPV6: + if (flow_key->eth.type != htons(ETH_P_IPV6)) + return -EINVAL; + + if (!flow_key->ip.proto) + return -EINVAL; + + ipv6_key = nla_data(ovs_key); + if (ipv6_key->ipv6_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv6_key->ipv6_frag != flow_key->ip.frag) + return -EINVAL; + + if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) + return -EINVAL; + + break; + case OVS_KEY_ATTR_TCP: if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL;