datapath: 64-bit GRE support
authorPravin B Shelar <pshelar@nicira.com>
Sat, 6 Oct 2012 00:55:02 +0000 (17:55 -0700)
committerPravin B Shelar <pshelar@nicira.com>
Sat, 6 Oct 2012 00:55:02 +0000 (17:55 -0700)
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@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
NEWS
datapath/tunnel.h
datapath/vport-gre.c
datapath/vport.c
datapath/vport.h
debian/ovs-monitor-ipsec
include/linux/openvswitch.h
lib/netdev-vport.c
vswitchd/vswitch.xml

diff --git a/NEWS b/NEWS
index 29fd9f3..d841cb3 100644 (file)
--- 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@openvswitch.org with
       concerns.
index 1924017..d2a87f2 100644 (file)
@@ -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)
index ab89c5b..6f406bc 100644 (file)
@@ -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,
+};
index dc7adfa..af1c066 100644 (file)
@@ -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
index 5cd3c18..61d5274 100644 (file)
@@ -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 */
index ffaa979..414d18b 100755 (executable)
@@ -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"))
index f5c9cca..294f6d0 100644 (file)
@@ -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
 };
 
index d5c288f..621abd1 100644 (file)
@@ -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 },
index a1b99f8..464afa1 100644 (file)
             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
     <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>