[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