[ovs-dev] [PATCH v3] 64-bit GRE support

Pravin B Shelar pshelar at nicira.com
Fri Oct 5 20:38:59 UTC 2012


v2-v3:
	- Fixed News item.
	- Fixed parsing order to be in sync with gre packet fields.
v1-v2:
	- Fixed according to comments from Ben and Jesse.

--8<--------------------------cut here-------------------------->8--
Extend GRE to have a 64-bit key. Use GRE sequence number to
store upper 32-bits of the key, but this is not standard way of
using GRE sequence number.

Bug #13186
Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
---
 NEWS                        |    1 +
 datapath/tunnel.h           |    3 +-
 datapath/vport-gre.c        |  125 +++++++++++++++++++++++++++++++++++--------
 datapath/vport.c            |    1 +
 datapath/vport.h            |    1 +
 debian/ovs-monitor-ipsec    |    2 +-
 include/linux/openvswitch.h |    1 +
 lib/netdev-vport.c          |   20 ++++++-
 vswitchd/vswitch.xml        |   18 ++++++-
 9 files changed, 146 insertions(+), 26 deletions(-)

diff --git a/NEWS b/NEWS
index 29fd9f3..d841cb3 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,7 @@ post-v1.8.0
       program was not included in distribution packaging.)
     - ovsdb-server now enforces the immutability of immutable columns.  This
       was not enforced in earlier versions due to an oversight.
+    - New support for a nonstandard form of GRE that supports a 64-bit key.
     - The following features are now deprecated.  They will be removed no
       earlier than February 2013.  Please email dev at openvswitch.org with
       concerns.
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index 1924017..d2a87f2 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -40,7 +40,8 @@
  * identifiers.
  */
 #define TNL_T_PROTO_GRE		0
-#define TNL_T_PROTO_CAPWAP	1
+#define TNL_T_PROTO_GRE64	1
+#define TNL_T_PROTO_CAPWAP	2
 
 /* These flags are only needed when calling tnl_find_port(). */
 #define TNL_T_KEY_EXACT		(1 << 10)
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index ab89c5b..6f406bc 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -54,12 +54,15 @@ static int gre_hdr_len(const struct tnl_mutable_config *mutable)
 	if (mutable->flags & TNL_F_CSUM)
 		len += GRE_HEADER_SECTION;
 
-	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
+	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION) {
 		len += GRE_HEADER_SECTION;
-
+		if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+			len += GRE_HEADER_SECTION;
+	}
 	return len;
 }
 
+
 /* Returns the least-significant 32 bits of a __be64. */
 static __be32 be64_get_low32(__be64 x)
 {
@@ -70,6 +73,15 @@ static __be32 be64_get_low32(__be64 x)
 #endif
 }
 
+static __be32 be64_get_high32(__be64 x)
+{
+#ifdef __BIG_ENDIAN
+	return (__force __be32)((__force u64)x >> 32);
+#else
+	return (__force __be32)x;
+#endif
+}
+
 static void gre_build_header(const struct vport *vport,
 			     const struct tnl_mutable_config *mutable,
 			     void *header)
@@ -86,11 +98,20 @@ static void gre_build_header(const struct vport *vport,
 		options++;
 	}
 
-	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
+	if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
 		greh->flags |= GRE_KEY;
+		if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+			greh->flags |= GRE_SEQ;
 
-	if (mutable->out_key)
+	} else if (mutable->out_key) {
+		greh->flags |= GRE_KEY;
 		*options = be64_get_low32(mutable->out_key);
+		if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+			options++;
+			*options = be64_get_high32(mutable->out_key);
+			greh->flags |= GRE_SEQ;
+		}
+	}
 }
 
 static struct sk_buff *gre_update_header(const struct vport *vport,
@@ -102,11 +123,19 @@ static struct sk_buff *gre_update_header(const struct vport *vport,
 					       - GRE_HEADER_SECTION);
 
 	/* Work backwards over the options so the checksum is last. */
-	if (mutable->flags & TNL_F_OUT_KEY_ACTION)
+	if (mutable->flags & TNL_F_OUT_KEY_ACTION) {
+		if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+			/* Set higher 32 bits to seq. */
+			*options = be64_get_high32(OVS_CB(skb)->tun_id);
+			options--;
+		}
 		*options = be64_get_low32(OVS_CB(skb)->tun_id);
-
-	if (mutable->out_key || mutable->flags & TNL_F_OUT_KEY_ACTION)
 		options--;
+	} else if (mutable->out_key) {
+		options--;
+		if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
+			options--;
+	}
 
 	if (mutable->flags & TNL_F_CSUM)
 		*(__sum16 *)options = csum_fold(skb_checksum(skb,
@@ -125,17 +154,17 @@ static struct sk_buff *gre_update_header(const struct vport *vport,
 	return skb;
 }
 
-/* Zero-extends a __be32 into the least-significant 32 bits of a __be64. */
-static __be64 be32_extend_to_be64(__be32 x)
+static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
 {
 #ifdef __BIG_ENDIAN
-	return (__force __be64)x;
+	return (__force __be64)((__force u64)seq << 32 | (__force u32)key);
 #else
-	return (__force __be64)((__force u64)x << 32);
+	return (__force __be64)((__force u64)key << 32 | (__force u32)seq);
 #endif
 }
 
-static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *key)
+static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
+			u32 *tunnel_type)
 {
 	/* IP and ICMP protocol handlers check that the IHL is valid. */
 	struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2));
@@ -158,14 +187,25 @@ static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *key)
 	}
 
 	if (greh->flags & GRE_KEY) {
-		hdr_len += GRE_HEADER_SECTION;
+		__be32 seq;
+		__be32 gre_key;
 
-		*key = be32_extend_to_be64(*options);
+		gre_key = *options;
+		hdr_len += GRE_HEADER_SECTION;
 		options++;
+
+		if (greh->flags & GRE_SEQ) {
+			seq = *options;
+			*tunnel_type = TNL_T_PROTO_GRE64;
+		} else {
+			seq = 0;
+			*tunnel_type = TNL_T_PROTO_GRE;
+		}
+		*tun_id = key_to_tunnel_id(gre_key, seq);
 	} else
-		*key = 0;
+		*tun_id = 0;
 
-	if (unlikely(greh->flags & GRE_SEQ))
+	if (greh->flags & GRE_SEQ)
 		hdr_len += GRE_HEADER_SECTION;
 
 	return hdr_len;
@@ -179,6 +219,7 @@ static void gre_err(struct sk_buff *skb, u32 info)
 	const int type = icmp_hdr(skb)->type;
 	const int code = icmp_hdr(skb)->code;
 	int mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
+	u32 tunnel_type;
 
 	struct iphdr *iph;
 	__be16 flags;
@@ -203,12 +244,12 @@ static void gre_err(struct sk_buff *skb, u32 info)
 	if (ipv4_is_multicast(iph->daddr))
 		return;
 
-	tunnel_hdr_len = parse_header(iph, &flags, &key);
+	tunnel_hdr_len = parse_header(iph, &flags, &key, &tunnel_type);
 	if (tunnel_hdr_len < 0)
 		return;
 
 	vport = ovs_tnl_find_port(dev_net(skb->dev), iph->saddr, iph->daddr, key,
-				  TNL_T_PROTO_GRE, &mutable);
+				  tunnel_type, &mutable);
 	if (!vport)
 		return;
 
@@ -329,14 +370,14 @@ static int gre_rcv(struct sk_buff *skb)
 	struct iphdr *iph;
 	__be16 flags;
 	__be64 key;
+	u32 tunnel_type;
 
 	if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr) + ETH_HLEN)))
 		goto error;
-
 	if (unlikely(!check_checksum(skb)))
 		goto error;
 
-	hdr_len = parse_header(ip_hdr(skb), &flags, &key);
+	hdr_len = parse_header(ip_hdr(skb), &flags, &key, &tunnel_type);
 	if (unlikely(hdr_len < 0))
 		goto error;
 
@@ -345,7 +386,7 @@ static int gre_rcv(struct sk_buff *skb)
 
 	iph = ip_hdr(skb);
 	vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr, key,
-				  TNL_T_PROTO_GRE, &mutable);
+				  tunnel_type, &mutable);
 	if (unlikely(!vport)) {
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 		goto error;
@@ -380,6 +421,19 @@ static struct vport *gre_create(const struct vport_parms *parms)
 	return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
 }
 
+static const struct tnl_ops gre64_tnl_ops = {
+	.tunnel_type	= TNL_T_PROTO_GRE64,
+	.ipproto	= IPPROTO_GRE,
+	.hdr_len	= gre_hdr_len,
+	.build_header	= gre_build_header,
+	.update_header	= gre_update_header,
+};
+
+static struct vport *gre_create64(const struct vport_parms *parms)
+{
+	return ovs_tnl_create(parms, &ovs_gre64_vport_ops, &gre64_tnl_ops);
+}
+
 static const struct net_protocol gre_protocol_handlers = {
 	.handler	=	gre_rcv,
 	.err_handler	=	gre_err,
@@ -388,10 +442,16 @@ static const struct net_protocol gre_protocol_handlers = {
 #endif
 };
 
+static bool inited;
+
 static int gre_init(void)
 {
 	int err;
 
+	if (inited)
+		return 0;
+
+	inited = true;
 	err = inet_add_protocol(&gre_protocol_handlers, IPPROTO_GRE);
 	if (err)
 		pr_warn("cannot register gre protocol handler\n");
@@ -401,6 +461,11 @@ static int gre_init(void)
 
 static void gre_exit(void)
 {
+	if (!inited)
+		return;
+
+	inited = false;
+
 	inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
 }
 
@@ -421,3 +486,21 @@ const struct vport_ops ovs_gre_vport_ops = {
 	.get_operstate	= ovs_vport_gen_get_operstate,
 	.send		= ovs_tnl_send,
 };
+
+const struct vport_ops ovs_gre64_vport_ops = {
+	.type		= OVS_VPORT_TYPE_GRE64,
+	.flags		= VPORT_F_TUN_ID,
+	.init		= gre_init,
+	.exit		= gre_exit,
+	.create		= gre_create64,
+	.destroy	= ovs_tnl_destroy,
+	.set_addr	= ovs_tnl_set_addr,
+	.get_name	= ovs_tnl_get_name,
+	.get_addr	= ovs_tnl_get_addr,
+	.get_options	= ovs_tnl_get_options,
+	.set_options	= ovs_tnl_set_options,
+	.get_dev_flags	= ovs_vport_gen_get_dev_flags,
+	.is_running	= ovs_vport_gen_is_running,
+	.get_operstate	= ovs_vport_gen_get_operstate,
+	.send		= ovs_tnl_send,
+};
diff --git a/datapath/vport.c b/datapath/vport.c
index dc7adfa..af1c066 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -41,6 +41,7 @@ static const struct vport_ops *base_vport_ops_list[] = {
 	&ovs_internal_vport_ops,
 	&ovs_patch_vport_ops,
 	&ovs_gre_vport_ops,
+	&ovs_gre64_vport_ops,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
 	&ovs_capwap_vport_ops,
 #endif
diff --git a/datapath/vport.h b/datapath/vport.h
index 5cd3c18..61d5274 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -254,6 +254,7 @@ extern const struct vport_ops ovs_netdev_vport_ops;
 extern const struct vport_ops ovs_internal_vport_ops;
 extern const struct vport_ops ovs_patch_vport_ops;
 extern const struct vport_ops ovs_gre_vport_ops;
+extern const struct vport_ops ovs_gre64_vport_ops;
 extern const struct vport_ops ovs_capwap_vport_ops;
 
 #endif /* vport.h */
diff --git a/debian/ovs-monitor-ipsec b/debian/ovs-monitor-ipsec
index ffaa979..414d18b 100755
--- a/debian/ovs-monitor-ipsec
+++ b/debian/ovs-monitor-ipsec
@@ -441,7 +441,7 @@ def main():
 
         new_interfaces = {}
         for rec in idl.tables["Interface"].rows.itervalues():
-            if rec.type == "ipsec_gre":
+            if rec.type == "ipsec_gre" or rec.type == "ipsec_gre64":
                 name = rec.name
                 options = rec.options
                 peer_cert_name = "ovs-%s.pem" % (options.get("remote_ip"))
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index f5c9cca..294f6d0 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -185,6 +185,7 @@ enum ovs_vport_type {
 	OVS_VPORT_TYPE_PATCH = 100, /* virtual tunnel connecting two vports */
 	OVS_VPORT_TYPE_GRE,      /* GRE tunnel */
 	OVS_VPORT_TYPE_CAPWAP,   /* CAPWAP tunnel */
+	OVS_VPORT_TYPE_GRE64 = 104, /* GRE tunnel with 64-bit keys */
 	__OVS_VPORT_TYPE_MAX
 };
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index d5c288f..621abd1 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -162,6 +162,14 @@ netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
         return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
                 ? "ipsec_gre" : "gre");
 
+    case OVS_VPORT_TYPE_GRE64:
+        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
+                                        a)) {
+            break;
+        }
+        return (nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
+                ? "ipsec_gre64" : "gre64");
+
     case OVS_VPORT_TYPE_CAPWAP:
         return "capwap";
 
@@ -583,9 +591,9 @@ parse_tunnel_config(const char *name, const char *type,
     uint32_t flags;
 
     flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE;
-    if (!strcmp(type, "gre")) {
+    if (!strcmp(type, "gre") || !strcmp(type, "gre64")) {
         is_gre = true;
-    } else if (!strcmp(type, "ipsec_gre")) {
+    } else if (!strcmp(type, "ipsec_gre") || !strcmp(type, "ipsec_gre64")) {
         is_gre = true;
         is_ipsec = true;
         flags |= TNL_F_IPSEC;
@@ -970,6 +978,14 @@ netdev_vport_register(void)
           { "ipsec_gre", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
           parse_tunnel_config, unparse_tunnel_config },
 
+        { OVS_VPORT_TYPE_GRE64,
+          { "gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+          parse_tunnel_config, unparse_tunnel_config },
+
+        { OVS_VPORT_TYPE_GRE64,
+          { "ipsec_gre64", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+          parse_tunnel_config, unparse_tunnel_config },
+
         { OVS_VPORT_TYPE_CAPWAP,
           { "capwap", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
           parse_tunnel_config, unparse_tunnel_config },
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index a1b99f8..464afa1 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1199,6 +1199,21 @@
             IPsec tunnel.
           </dd>
 
+          <dt><code>gre64</code></dt>
+          <dd>
+            It is same as GRE, but it allows 64 bit key. To store higher 32-bits
+            of key, it uses GRE protocol sequence number field. This is non
+            standard use of GRE protocol since OVS does not increment
+            sequence number for every packet at time of encap as expected by
+            standard GRE implementation. See <ref group="Tunnel Options"/>
+            for information on configuring GRE tunnels.
+          </dd>
+
+          <dt><code>ipsec_gre64</code></dt>
+          <dd>
+            Same as IPSEC_GRE except 64 bit key.
+          </dd>
+
           <dt><code>capwap</code></dt>
           <dd>
             An Ethernet tunnel over the UDP transport portion of CAPWAP (RFC
@@ -1224,7 +1239,8 @@
     <group title="Tunnel Options">
       <p>
         These options apply to interfaces with <ref column="type"/> of
-        <code>gre</code>, <code>ipsec_gre</code>, and <code>capwap</code>.
+        <code>gre</code>, <code>ipsec_gre</code>, <code>gre64</code>,
+        <code>ipsec_gre64</code>, and <code>capwap</code>.
       </p>
 
       <p>
-- 
1.7.10




More information about the dev mailing list