2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-04 08:15:25 +00:00

datapath: 64-bit GRE support

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>
This commit is contained in:
Pravin B Shelar
2012-10-05 17:55:02 -07:00
parent a457574880
commit 2de795adb9
9 changed files with 147 additions and 27 deletions

1
NEWS
View File

@@ -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.

View 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)

View 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)
*options = be64_get_low32(OVS_CB(skb)->tun_id);
if (mutable->out_key || 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);
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) {
__be32 seq;
__be32 gre_key;
gre_key = *options;
hdr_len += GRE_HEADER_SECTION;
*key = be32_extend_to_be64(*options);
options++;
} else
*key = 0;
if (unlikely(greh->flags & GRE_SEQ))
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
*tun_id = 0;
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,
};

View 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

View 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 */

View File

@@ -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"))

View 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
};

View 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 },

View File

@@ -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>