[ovs-dev] [PATCH v2] datapath: Add support for VXLAN tunnels to Open vSwitch

Kyle Mestery kmestery at cisco.com
Fri Nov 16 22:43:42 UTC 2012


Add support for VXLAN tunnels to Open vSwitch. Add support
for setting the destination UDP port on a per-port basis.
This is done by adding a "dst_port" parameter to the port
configuration. This is only applicable currently to VXLAN
tunnels.

The only change in this version of the patch is around the algorithm used
to select the source port when transmitting packets. On a suggestion from
Jesse, I've taken the one used by the upstream Linux version of VXLAN
and added it here, with a small change to ensure zeroing of the source UDP
port before calling it to ensure the hash is consistent and doesn't explode
flows in the fast path.

This patch set is based on one posted by Ben Pfaff on Oct. 12, 2011
to the ovs-dev mailing list:

http://openvswitch.org/pipermail/dev/2011-October/012051.html

The patch has been maintained, updated, and freshened by me and is
availalbe at the following github repository:

https://github.com/mestery/ovs-vxlan/tree/vxlan

I've tested this patch with multiple VXLAN tunnels between hosts
using different UDP port numbers.

See the following IETF draft for additional information about VXLAN:
http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02

Signed-off-by: Kyle Mestery <kmestery at cisco.com>
---
 NEWS                             |   3 +
 README                           |   2 +-
 datapath/Modules.mk              |   3 +-
 datapath/linux/.gitignore        |   2 +
 datapath/tunnel.c                |   7 +
 datapath/tunnel.h                |   2 +
 datapath/vport-vxlan.c           | 478 +++++++++++++++++++++++++++++++++++++++
 datapath/vport.c                 |   1 +
 datapath/vport.h                 |   1 +
 debian/control                   |   2 +-
 debian/openvswitch-ipsec.init    |   5 +-
 debian/ovs-monitor-ipsec         | 110 +++++----
 include/linux/openvswitch.h      |   1 +
 include/openflow/nicira-ext.h    |   8 +-
 include/openvswitch/tunnel.h     |   1 +
 lib/netdev-vport.c               |  27 +++
 rhel/etc_init.d_openvswitch      |   2 +
 tests/ovs-monitor-ipsec.at       |  67 ++++++
 vswitchd/vswitch.xml             |  59 ++++-
 xenserver/etc_init.d_openvswitch |   2 +
 20 files changed, 723 insertions(+), 60 deletions(-)
 create mode 100644 datapath/vport-vxlan.c

diff --git a/NEWS b/NEWS
index 0372965..a98c194 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ post-v1.9.0
 
 v1.9.0 - xx xxx xxxx
 --------------------
+    - New support for the experimental VXLAN tunnel protocol (see
+      http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02)
+      and VXLAN over IPSEC.
     - The tunneling code no longer assumes input and output keys are symmetric.
       If they are not, PMTUD needs to be disabled for tunneling to work. Note
       this only applies to flow-based keys.
diff --git a/README b/README
index 7c680d4..d7691a0 100644
--- a/README
+++ b/README
@@ -24,7 +24,7 @@ vSwitch supports the following features:
     * NIC bonding with or without LACP on upstream switch
     * NetFlow, sFlow(R), and mirroring for increased visibility
     * QoS (Quality of Service) configuration, plus policing
-    * GRE, GRE over IPSEC, and CAPWAP tunneling
+    * GRE, GRE over IPSEC, CAPWAP, VXLAN, and VXLAN over IPSEC tunneling
     * 802.1ag connectivity fault management
     * OpenFlow 1.0 plus numerous extensions
     * Transactional configuration database with C and Python bindings
diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index 24c1075..24e6559 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -26,7 +26,8 @@ openvswitch_sources = \
 	vport-gre.c \
 	vport-internal_dev.c \
 	vport-netdev.c \
-	vport-patch.c
+	vport-patch.c \
+	vport-vxlan.c
 
 openvswitch_headers = \
 	checksum.h \
diff --git a/datapath/linux/.gitignore b/datapath/linux/.gitignore
index d6de397..968948c 100644
--- a/datapath/linux/.gitignore
+++ b/datapath/linux/.gitignore
@@ -23,6 +23,7 @@
 /kmemdup.c
 /loop_counter.c
 /modules.order
+/net_namespace.c
 /netdevice.c
 /net_namespace.c
 /random32.c
@@ -39,5 +40,6 @@
 /vport-internal_dev.c
 /vport-netdev.c
 /vport-patch.c
+/vport-vxlan.c
 /vport.c
 /workqueue.c
diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index fb4854a..05a73df 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -1042,6 +1042,7 @@ static const struct nla_policy tnl_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
 	[OVS_TUNNEL_ATTR_IN_KEY]   = { .type = NLA_U64 },
 	[OVS_TUNNEL_ATTR_TOS]      = { .type = NLA_U8 },
 	[OVS_TUNNEL_ATTR_TTL]      = { .type = NLA_U8 },
+	[OVS_TUNNEL_ATTR_DST_PORT] = { .type = NLA_U16 },
 };
 
 /* Sets OVS_TUNNEL_ATTR_* fields in 'mutable', which must initially be
@@ -1087,6 +1088,9 @@ static int tnl_set_config(struct net *net, struct nlattr *options,
 	if (a[OVS_TUNNEL_ATTR_TTL])
 		mutable->ttl = nla_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
 
+	if (a[OVS_TUNNEL_ATTR_DST_PORT])
+		mutable->dst_port = nla_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+
 	if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
 		mutable->key.tunnel_type |= TNL_T_KEY_MATCH;
 		mutable->flags |= TNL_F_IN_KEY_MATCH;
@@ -1242,6 +1246,9 @@ int ovs_tnl_get_options(const struct vport *vport, struct sk_buff *skb)
 		goto nla_put_failure;
 	if (mutable->ttl && nla_put_u8(skb, OVS_TUNNEL_ATTR_TTL, mutable->ttl))
 		goto nla_put_failure;
+	if (mutable->dst_port && nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT,
+					     mutable->dst_port))
+		goto nla_put_failure;
 
 	return 0;
 
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index c268057..c0b50e7 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -42,6 +42,7 @@
 #define TNL_T_PROTO_GRE		0
 #define TNL_T_PROTO_GRE64	1
 #define TNL_T_PROTO_CAPWAP	2
+#define TNL_T_PROTO_VXLAN	3
 
 /* These flags are only needed when calling tnl_find_port(). */
 #define TNL_T_KEY_EXACT		(1 << 10)
@@ -116,6 +117,7 @@ struct tnl_mutable_config {
 	u32	flags;
 	u8	tos;
 	u8	ttl;
+	u16	dst_port;
 
 	/* Multicast configuration. */
 	int	mlink;
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
new file mode 100644
index 0000000..4926c17
--- /dev/null
+++ b/datapath/vport-vxlan.c
@@ -0,0 +1,478 @@
+ /*
+ * Copyright (c) 2011 Nicira, Inc.
+ * Copyright (c) 2012 Cisco Systems, Inc.
+ * Distributed under the terms of the GNU GPL version 2.
+ *
+ * Significant portions of this file may be copied from parts of the Linux
+ * kernel, by Linus Torvalds and others.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/udp.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/udp.h>
+
+#include "datapath.h"
+#include "tunnel.h"
+#include "vport.h"
+#include "vport-generic.h"
+
+#define VXLAN_DST_PORT 8472
+#define VXLAN_IPSEC_SRC_PORT 4564
+
+#define VXLAN_FLAGS 0x08000000  /* struct vxlanhdr.vx_flags required value. */
+
+/**
+ * struct vxlanhdr - VXLAN header
+ * @vx_flags: Must have the exact value %VXLAN_FLAGS.
+ * @vx_vni: VXLAN Network Identifier (VNI) in top 24 bits, low 8 bits zeroed.
+ */
+struct vxlanhdr {
+	__be32 vx_flags;
+	__be32 vx_vni;
+};
+
+#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
+
+static inline int vxlan_hdr_len(const struct tnl_mutable_config *mutable,
+				const struct ovs_key_ipv4_tunnel *tun_key)
+{
+	return VXLAN_HLEN;
+}
+
+/**
+ * struct vxlan_port - Keeps track of open UDP ports
+ * @port: The UDP port number.
+ * @socket: The socket created for this port number.
+ * @count: How many ports are using this socket/port.
+ * @hash_node: Hash node.
+ */
+struct vxlan_port {
+	u16 port;
+	struct socket *vxlan_rcv_socket;
+	int count;
+
+	/* Protected by RTNL lock. */
+	struct hlist_node hash_node;
+};
+
+/* Protected by RTNL lock. */
+static struct hlist_head *vxlan_ports;
+#define VXLAN_SOCK_HASH_BUCKETS 64
+
+/**
+ * struct vxlan_if - Maps port names to UDP port numbers
+ * @port: The UDP port number this interface is using.
+ * @ifname: The name of the interface.
+ * @hash_node: Hash node.
+ */
+struct vxlan_if {
+	u16 port;
+	char ifname[IFNAMSIZ];
+
+	/* Protected by RTNL lock. */
+	struct hlist_node hash_node;
+};
+
+/* Protected by RTNL lock. */
+static struct hlist_head *vxlan_ifs;
+#define VXLAN_IF_HASH_BUCKETS 64
+
+static struct hlist_head *vxlan_hash_bucket(struct net *net, u16 port)
+{
+	unsigned int hash = jhash(&port, sizeof(port), (unsigned long) net);
+	return &vxlan_ports[hash & (VXLAN_SOCK_HASH_BUCKETS - 1)];
+}
+
+static struct vxlan_port *vxlan_port_exists(struct net *net, u16 port)
+{
+	struct hlist_head *bucket = vxlan_hash_bucket(net, port);
+	struct vxlan_port *vxlan_port;
+	struct hlist_node *node;
+
+	hlist_for_each_entry(vxlan_port, node, bucket, hash_node) {
+		if (vxlan_port->port == port)
+			return vxlan_port;
+	}
+
+	return NULL;
+}
+
+static struct hlist_head *vxlanif_hash_bucket(struct net *net, const char *name)
+{
+	unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
+	return &vxlan_ifs[hash & (VXLAN_IF_HASH_BUCKETS - 1)];
+}
+
+static struct vxlan_if *vxlan_if_by_name(struct net *net, const char *name)
+{
+	struct hlist_head *bucket = vxlanif_hash_bucket(net, name);
+	struct vxlan_if *vxlan_if;
+	struct hlist_node *node;
+
+	hlist_for_each_entry(vxlan_if, node, bucket, hash_node) {
+		if (!strcmp(vxlan_if->ifname, name))
+			return vxlan_if;
+	}
+
+	return NULL;
+}
+
+static inline struct vxlanhdr *vxlan_hdr(const struct sk_buff *skb)
+{
+	return (struct vxlanhdr *)(udp_hdr(skb) + 1);
+}
+
+/* The below used as the min/max for the UDP port range */
+#define VXLAN_SRC_PORT_MIN      32768
+#define VXLAN_SRC_PORT_MAX      61000
+
+/* Compute source port for outgoing packet
+ * first choice to use L4 flow hash since it will spread
+ * better and maybe available from hardware
+ * secondary choice is to use jhash on the Ethernet header
+ */
+static u16 get_src_port(struct sk_buff *skb,
+			const struct tnl_mutable_config *mutable)
+{
+	unsigned int range = (VXLAN_SRC_PORT_MAX - VXLAN_SRC_PORT_MIN) + 1;
+	u32 hash;
+
+	hash = skb_get_rxhash(skb);
+	if (!hash)
+		hash = jhash(skb->data, 2 * ETH_ALEN,
+			     (__force u32) skb->protocol);
+
+	return (__force u16)(((u64) hash * range) >> 32) + VXLAN_SRC_PORT_MIN;
+}
+
+static struct sk_buff *vxlan_build_header(const struct vport *vport,
+					  const struct tnl_mutable_config *mutable,
+					  struct dst_entry *dst,
+					  struct sk_buff *skb,
+					  int tunnel_hlen)
+{
+	struct udphdr *udph = udp_hdr(skb);
+	struct vxlanhdr *vxh = (struct vxlanhdr *)(udph + 1);
+	const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
+	__be64 out_key;
+
+	if (tun_key->ipv4_dst)
+		out_key = tun_key->tun_id;
+	else
+		out_key = mutable->out_key;
+
+	if (mutable->dst_port)
+		udph->dest = htons(mutable->dst_port);
+	else
+		udph->dest = htons(VXLAN_DST_PORT);
+	/* Clear this out since get_src_port() below calls skb_get_rxhash(),
+	 * which includes source UDP port in the hash itself.
+	 */
+	udph->source = 0;
+	udph->source = htons(get_src_port(skb, mutable));
+	udph->check = 0;
+	udph->len = htons(skb->len - skb_transport_offset(skb));
+
+	vxh->vx_flags = htonl(VXLAN_FLAGS);
+	vxh->vx_vni = htonl(be64_to_cpu(out_key) << 8);
+
+	/*
+	 * Allow our local IP stack to fragment the outer packet even if the
+	 * DF bit is set as a last resort.  We also need to force selection of
+	 * an IP ID here because Linux will otherwise leave it at 0 if the
+	 * packet originally had DF set.
+	 */
+	skb->local_df = 1;
+	__ip_select_ident(ip_hdr(skb), dst, 0);
+
+	return skb;
+}
+
+/* Called with rcu_read_lock and BH disabled. */
+static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
+{
+	struct vport *vport;
+	struct vxlanhdr *vxh;
+	const struct tnl_mutable_config *mutable;
+	struct iphdr *iph;
+	struct ovs_key_ipv4_tunnel tun_key;
+	int tunnel_type;
+	__be64 key;
+	u32 tunnel_flags = 0;
+
+	if (unlikely(!pskb_may_pull(skb, VXLAN_HLEN + ETH_HLEN)))
+		goto error;
+
+	vxh = vxlan_hdr(skb);
+	if (unlikely(vxh->vx_flags != htonl(VXLAN_FLAGS) ||
+		     vxh->vx_vni & htonl(0xff)))
+		goto error;
+
+	__skb_pull(skb, VXLAN_HLEN);
+	skb_postpull_rcsum(skb, skb_transport_header(skb), VXLAN_HLEN + ETH_HLEN);
+
+	key = cpu_to_be64(ntohl(vxh->vx_vni) >> 8);
+
+	tunnel_type = TNL_T_PROTO_VXLAN;
+
+	iph = ip_hdr(skb);
+	vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
+		key, tunnel_type, &mutable);
+	if (unlikely(!vport)) {
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+		goto error;
+	}
+
+	if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
+		tunnel_flags = OVS_TNL_F_KEY;
+	else
+		key = 0;
+
+	/* Save outer tunnel values */
+	tnl_tun_key_init(&tun_key, iph, key, tunnel_flags);
+	OVS_CB(skb)->tun_key = &tun_key;
+
+	ovs_tnl_rcv(vport, skb);
+	goto out;
+
+error:
+	kfree_skb(skb);
+out:
+	return 0;
+}
+
+/* Random value.  Irrelevant as long as it's not 0 since we set the handler. */
+#define UDP_ENCAP_VXLAN 10
+static int vxlan_socket_init(struct vxlan_port *vxlan_port)
+{
+	int err;
+	struct sockaddr_in sin;
+
+	err = sock_create(AF_INET, SOCK_DGRAM, 0, &vxlan_port->vxlan_rcv_socket);
+	if (err)
+		goto error;
+
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = htonl(INADDR_ANY);
+	sin.sin_port = htons(vxlan_port->port);
+
+	err = kernel_bind(vxlan_port->vxlan_rcv_socket, (struct sockaddr *)&sin,
+			  sizeof(struct sockaddr_in));
+	if (err)
+		goto error_sock;
+
+	udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_type = UDP_ENCAP_VXLAN;
+	udp_sk(vxlan_port->vxlan_rcv_socket->sk)->encap_rcv = vxlan_rcv;
+
+	udp_encap_enable();
+
+	return 0;
+
+error_sock:
+	sock_release(vxlan_port->vxlan_rcv_socket);
+error:
+	pr_warn("cannot register vxlan protocol handler\n");
+	return err;
+}
+
+static const struct nla_policy vxlan_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
+	[OVS_TUNNEL_ATTR_DST_PORT] = { .type = NLA_U16 },
+};
+
+static int vxlan_tunnel_setup(struct net *net, const char *linkname,
+			     struct nlattr *options)
+{
+	struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
+	int err;
+	u16 dst_port;
+	struct vxlan_port *vxlan_port;
+	struct vxlan_if *vxlan_if;
+
+	if (!options) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = nla_parse_nested(a, OVS_TUNNEL_ATTR_MAX, options, vxlan_policy);
+	if (err)
+		goto out;
+
+	if (a[OVS_TUNNEL_ATTR_DST_PORT])
+		dst_port = nla_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+	else
+		dst_port = VXLAN_DST_PORT;
+
+	/* Verify if we already have a socket created for this port */
+	vxlan_port = vxlan_port_exists(net, dst_port);
+	if (vxlan_port) {
+		vxlan_port->count++;
+		err = 0;
+		goto out;
+	}
+
+	/* Add a new socket for this port */
+	vxlan_port = kmalloc(sizeof(struct vxlan_port), GFP_KERNEL);
+	if (!vxlan_port) {
+		err = -ENOMEM;
+		goto out;
+	}
+	memset (vxlan_port, 0, sizeof(struct vxlan_port));
+
+	vxlan_port->port = dst_port;
+	vxlan_port->count++;
+	hlist_add_head(&vxlan_port->hash_node,
+		       vxlan_hash_bucket(net, dst_port));
+
+	err = vxlan_socket_init(vxlan_port);
+	if (err)
+		goto error_vxlan_if;
+
+	vxlan_if = kmalloc(sizeof(struct vxlan_if), GFP_KERNEL);
+	if (!vxlan_if) {
+		err = -ENOMEM;
+		goto error_vxlan_if;
+	}
+	memset(vxlan_if, 0, sizeof(*vxlan_if));
+
+	vxlan_if->port = dst_port;
+	memcpy(vxlan_if->ifname, linkname, IFNAMSIZ);
+	hlist_add_head(&vxlan_if->hash_node,
+		       vxlanif_hash_bucket(net, linkname));
+
+out:
+	return err;
+error_vxlan_if:
+	hlist_del(&vxlan_port->hash_node);
+	kfree(vxlan_port);
+	goto out;
+}
+
+static int vxlan_set_options(struct vport *vport, struct nlattr *options)
+{
+	int err;
+	const char *vname = vport->ops->get_name(vport);
+
+	err = vxlan_tunnel_setup(ovs_dp_get_net(vport->dp), vname, options);
+	if (err)
+		goto out;
+
+	err = ovs_tnl_set_options(vport, options);
+
+out:
+	return err;
+}
+
+static const struct tnl_ops ovs_vxlan_tnl_ops = {
+	.tunnel_type	= TNL_T_PROTO_VXLAN,
+	.ipproto	= IPPROTO_UDP,
+	.hdr_len	= vxlan_hdr_len,
+	.build_header	= vxlan_build_header,
+};
+
+static const struct tnl_ops ovs_ipsec_vxlan_tnl_ops = {
+	.tunnel_type	= TNL_T_PROTO_VXLAN,
+	.ipproto	= IPPROTO_UDP,
+	.hdr_len	= vxlan_hdr_len,
+	.build_header	= vxlan_build_header,
+};
+
+void vxlan_tnl_destroy(struct vport *vport)
+{
+	struct vxlan_if *vxlan_if;
+	struct vxlan_port *vxlan_port;
+	const char *vname = vport->ops->get_name(vport);
+
+	vxlan_if = vxlan_if_by_name(ovs_dp_get_net(vport->dp), vname);
+	if (!vxlan_if)
+		goto out;
+
+	vxlan_port = vxlan_port_exists(ovs_dp_get_net(vport->dp),
+					 vxlan_if->port);
+	if (!vxlan_port)
+		goto out_if;
+
+	if (!--vxlan_port->count) {
+		sock_release(vxlan_port->vxlan_rcv_socket);
+		hlist_del(&vxlan_port->hash_node);
+		kfree(vxlan_port);
+	}
+
+out_if:
+	hlist_del(&vxlan_if->hash_node);
+	kfree(vxlan_if);
+out:
+	ovs_tnl_destroy(vport);
+}
+
+static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
+{
+	int err;
+
+	err = vxlan_tunnel_setup(ovs_dp_get_net(parms->dp), parms->name,
+						parms->options);
+	return ovs_tnl_create(parms, &ovs_vxlan_vport_ops, &ovs_vxlan_tnl_ops);
+}
+
+static int vxlan_init(void)
+{
+	int err;
+
+	vxlan_ifs = kzalloc(VXLAN_IF_HASH_BUCKETS * sizeof(struct hlist_head),
+			    GFP_KERNEL);
+	if (!vxlan_ifs) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	vxlan_ports = kzalloc(VXLAN_SOCK_HASH_BUCKETS * sizeof(struct hlist_head),
+				GFP_KERNEL);
+	if (!vxlan_ports) {
+		err = -ENOMEM;
+		goto free_ifs;
+	}
+
+out:
+	return 0;
+free_ifs:
+	kfree(vxlan_ifs);
+	goto out;
+}
+
+static void vxlan_exit(void)
+{
+	kfree(vxlan_ports);
+	kfree(vxlan_ifs);
+}
+
+const struct vport_ops ovs_vxlan_vport_ops = {
+	.type		= OVS_VPORT_TYPE_VXLAN,
+	.flags		= VPORT_F_TUN_ID,
+	.init		= vxlan_init,
+	.exit		= vxlan_exit,
+	.create		= vxlan_tnl_create,
+	.destroy	= vxlan_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	= vxlan_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,
+};
+#else
+#warning VXLAN tunneling will not be available on kernels before 2.6.26
+#endif /* Linux kernel < 2.6.26 */
diff --git a/datapath/vport.c b/datapath/vport.c
index 4934ac1..a1c7542 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -45,6 +45,7 @@ static const struct vport_ops *base_vport_ops_list[] = {
 	&ovs_gre64_vport_ops,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
 	&ovs_capwap_vport_ops,
+	&ovs_vxlan_vport_ops,
 #endif
 };
 
diff --git a/datapath/vport.h b/datapath/vport.h
index 5a7caf5..5080629 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -257,5 +257,6 @@ extern const struct vport_ops ovs_gre_vport_ops;
 extern const struct vport_ops ovs_gre_ft_vport_ops;
 extern const struct vport_ops ovs_gre64_vport_ops;
 extern const struct vport_ops ovs_capwap_vport_ops;
+extern const struct vport_ops ovs_vxlan_vport_ops;
 
 #endif /* vport.h */
diff --git a/debian/control b/debian/control
index 15ca3eb..da8a16d 100644
--- a/debian/control
+++ b/debian/control
@@ -98,7 +98,7 @@ Description: Open vSwitch GRE-over-IPsec support
  1000V.
  .
  The ovs-monitor-ipsec script provides support for encrypting GRE
- tunnels with IPsec.
+ and VXLAN tunnels with IPsec.
 
 Package: openvswitch-pki
 Architecture: all
diff --git a/debian/openvswitch-ipsec.init b/debian/openvswitch-ipsec.init
index 8e5c7b2..9e29a6e 100755
--- a/debian/openvswitch-ipsec.init
+++ b/debian/openvswitch-ipsec.init
@@ -1,5 +1,6 @@
 #!/bin/sh
 #
+# Copyright (c) 2011 Nicira, Inc.
 # Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <jfs at debian.org>
 #
 # This is free software; you may redistribute it and/or modify
@@ -23,9 +24,9 @@
 # Required-Stop:     $remote_fs
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
-# Short-Description: Open vSwitch GRE-over-IPsec daemon
+# Short-Description: Open vSwitch IPsec tunnel daemon
 # Description:       The ovs-monitor-ipsec script provides support for encrypting GRE
-#                    tunnels with IPsec.
+#                    and VXLAN tunnels with IPsec.
 ### END INIT INFO
 
 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
diff --git a/debian/ovs-monitor-ipsec b/debian/ovs-monitor-ipsec
index 414d18b..9acfd30 100755
--- a/debian/ovs-monitor-ipsec
+++ b/debian/ovs-monitor-ipsec
@@ -14,9 +14,10 @@
 # limitations under the License.
 
 
-# A daemon to monitor attempts to create GRE-over-IPsec tunnels.
-# Uses racoon and setkey to support the configuration.  Assumes that
-# OVS has complete control over IPsec configuration for the box.
+# A daemon to monitor attempts to create tunnels over IPsec.
+# Racoon and setkey are used to support the configuration.  It is
+# assumed that OVS has complete control over IPsec configuration for
+# the box.
 
 # xxx To-do:
 #  - Doesn't actually check that Interface is connected to bridge
@@ -44,6 +45,10 @@ import ovs.vlog
 vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
 root_prefix = ''                # Prefix for absolute file names, for testing.
 SETKEY = "/usr/sbin/setkey"
+# UDP ports used for VXLAN.  The source port is only fixed for
+# VXLAN-over-IPsec traffic.
+VXLAN_DST_PORT = 4563
+VXLAN_SRC_PORT = 4564
 exiting = False
 
 
@@ -257,7 +262,7 @@ path certificate "%s";
 
 # Class to configure IPsec on a system using racoon for IKE and setkey
 # for maintaining the Security Association Database (SAD) and Security
-# Policy Database (SPD).  Only policies for GRE are supported.
+# Policy Database (SPD).  Only policies for GRE and VXLAN are supported.
 class IPsec:
     def __init__(self):
         self.sad_flush()
@@ -323,26 +328,42 @@ class IPsec:
         self.call_setkey("spdflush;\n")
 
     def spd_add(self, local_ip, remote_ip):
-        cmds = ("spdadd %s %s gre -P out ipsec esp/transport//require;\n" %
-                    (local_ip, remote_ip))
-        cmds += ("spdadd %s %s gre -P in ipsec esp/transport//require;\n" %
-                    (remote_ip, local_ip))
+        tunnel_type = self.entries[remote_ip]
+        if tunnel_type == "vxlan":
+            cmds = ("spdadd %s[%s] %s[%s] udp -P out ipsec esp/transport//require;\n"
+                        % (local_ip, VXLAN_SRC_PORT, remote_ip, VXLAN_DST_PORT))
+            cmds += ("spdadd %s[%s] %s[%s] udp -P in ipsec esp/transport//require;\n"
+                        % (remote_ip, VXLAN_DST_PORT, local_ip, VXLAN_SRC_PORT))
+        else:
+            cmds = ("spdadd %s %s gre -P out ipsec esp/transport//require;\n" %
+                        (local_ip, remote_ip))
+            cmds += ("spdadd %s %s gre -P in ipsec esp/transport//require;\n" %
+                        (remote_ip, local_ip))
         self.call_setkey(cmds)
 
     def spd_del(self, local_ip, remote_ip):
-        cmds = "spddelete %s %s gre -P out;\n" % (local_ip, remote_ip)
-        cmds += "spddelete %s %s gre -P in;\n" % (remote_ip, local_ip)
+        tunnel_type = self.entries[remote_ip]
+        if tunnel_type == "vxlan":
+            cmds = ("spddelete %s %s udp -P out;\n" % (local_ip, remote_ip))
+            cmds += ("spddelete %s %s udp -P in;\n" % (remote_ip, local_ip))
+        else:
+            cmds = "spddelete %s %s gre -P out;\n" % (local_ip, remote_ip)
+            cmds += "spddelete %s %s gre -P in;\n" % (remote_ip, local_ip)
         self.call_setkey(cmds)
 
     def add_entry(self, local_ip, remote_ip, vals):
+        tunnel_type = vals["tunnel_type"]
+        if tunnel_type not in ("gre", "vxlan"):
+            raise error.Error("unknown tunnel type: %s" % tunnel_type)
+
         if remote_ip in self.entries:
             raise error.Error("host %s already configured for ipsec"
                               % remote_ip)
 
         self.racoon.add_entry(remote_ip, vals)
-        self.spd_add(local_ip, remote_ip)
 
-        self.entries.append(remote_ip)
+        self.entries[remote_ip] = tunnel_type
+        self.spd_add(local_ip, remote_ip)
 
     def del_entry(self, local_ip, remote_ip):
         if remote_ip in self.entries:
@@ -350,7 +371,7 @@ class IPsec:
             self.spd_del(local_ip, remote_ip)
             self.sad_del(local_ip, remote_ip)
 
-            self.entries.remove(remote_ip)
+            del self.entries[remote_ip]
 
 
 def update_ipsec(ipsec, interfaces, new_interfaces):
@@ -442,38 +463,45 @@ def main():
         new_interfaces = {}
         for rec in idl.tables["Interface"].rows.itervalues():
             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"))
-                entry = {
-                    "remote_ip": options.get("remote_ip"),
-                    "local_ip": options.get("local_ip", "0.0.0.0/0"),
-                    "certificate": options.get("certificate"),
-                    "private_key": options.get("private_key"),
-                    "use_ssl_cert": options.get("use_ssl_cert"),
-                    "peer_cert": options.get("peer_cert"),
-                    "peer_cert_file": Racoon.cert_dir + "/" + peer_cert_name,
-                    "psk": options.get("psk")}
-
-                if entry["peer_cert"] and entry["psk"]:
-                    vlog.warn("both 'peer_cert' and 'psk' defined for %s"
+                tunnel_type = "gre"
+            elif rec.type == "ipsec_vxlan":
+                tunnel_type = "vxlan"
+            else:
+                continue
+
+            name = rec.name
+            options = rec.options
+            peer_cert_name = "ovs-%s.pem" % (options.get("remote_ip"))
+            entry = {
+                "remote_ip": options.get("remote_ip"),
+                "local_ip": options.get("local_ip", "0.0.0.0/0"),
+                "certificate": options.get("certificate"),
+                "private_key": options.get("private_key"),
+                "use_ssl_cert": options.get("use_ssl_cert"),
+                "peer_cert": options.get("peer_cert"),
+                "peer_cert_file": Racoon.cert_dir + "/" + peer_cert_name,
+                "psk": options.get("psk")},
+                "tunnel_type": tunnel_type }
+
+            if entry["peer_cert"] and entry["psk"]:
+                vlog.warn("both 'peer_cert' and 'psk' defined for %s"
                               % name)
-                    continue
-                elif not entry["peer_cert"] and not entry["psk"]:
-                    vlog.warn("no 'peer_cert' or 'psk' defined for %s" % name)
-                    continue
+                continue
+            elif not entry["peer_cert"] and not entry["psk"]:
+                vlog.warn("no 'peer_cert' or 'psk' defined for %s" % name)
+                continue
 
-                # The "use_ssl_cert" option is deprecated and will
-                # likely go away in the near future.
-                if entry["use_ssl_cert"] == "true":
-                    if not ssl_cert:
-                        vlog.warn("no valid SSL entry for %s" % name)
-                        continue
+            # The "use_ssl_cert" option is deprecated and will
+            # likely go away in the near future.
+            if entry["use_ssl_cert"] == "true":
+                if not ssl_cert:
+                    vlog.warn("no valid SSL entry for %s" % name)
+                    continue
 
-                    entry["certificate"] = ssl_cert[0]
-                    entry["private_key"] = ssl_cert[1]
+                entry["certificate"] = ssl_cert[0]
+                entry["private_key"] = ssl_cert[1]
 
-                new_interfaces[name] = entry
+            new_interfaces[name] = entry
 
         if interfaces != new_interfaces:
             update_ipsec(ipsec, interfaces, new_interfaces)
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index c4823d9..c5b72b3 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -186,6 +186,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_VXLAN,    /* VXLAN tunnel */
 	OVS_VPORT_TYPE_GRE64 = 104, /* GRE tunnel with 64-bit keys */
 	__OVS_VPORT_TYPE_MAX
 };
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 88eba19..11b761d 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1578,9 +1578,11 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 
 /* Tunnel ID.
  *
- * For a packet received via GRE tunnel including a (32-bit) key, the key is
- * stored in the low 32-bits and the high bits are zeroed.  For other packets,
- * the value is 0.
+ * For a packet received via a GRE or VXLAN tunnel including a (32-bit) key, the
+ * key is stored in the low 32-bits and the high bits are zeroed.  For other
+ * packets, the value is 0.
+ *
+ * All zero bits, for packets not received via a keyed tunnel.
  *
  * Prereqs: None.
  *
diff --git a/include/openvswitch/tunnel.h b/include/openvswitch/tunnel.h
index 42c3621..23d8ba7 100644
--- a/include/openvswitch/tunnel.h
+++ b/include/openvswitch/tunnel.h
@@ -57,6 +57,7 @@ enum {
 	OVS_TUNNEL_ATTR_IN_KEY,   /* __be64 key to match on input. */
 	OVS_TUNNEL_ATTR_TOS,      /* 8-bit TOS value. */
 	OVS_TUNNEL_ATTR_TTL,      /* 8-bit TTL value. */
+	OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by VXLAN. */
 	__OVS_TUNNEL_ATTR_MAX
 };
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 5171171..734932e 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -173,6 +173,14 @@ netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
     case OVS_VPORT_TYPE_CAPWAP:
         return "capwap";
 
+    case OVS_VPORT_TYPE_VXLAN:
+        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_vxlan" : "vxlan");
+
     case OVS_VPORT_TYPE_FT_GRE:
     case __OVS_VPORT_TYPE_MAX:
         break;
@@ -584,6 +592,7 @@ parse_tunnel_config(const char *name, const char *type,
                     const struct smap *args, struct ofpbuf *options)
 {
     bool is_gre = false;
+    bool is_vxlan = false;
     bool is_ipsec = false;
     struct smap_node *node;
     bool ipsec_mech_set = false;
@@ -602,6 +611,8 @@ parse_tunnel_config(const char *name, const char *type,
         is_gre = true;
         is_ipsec = true;
         flags |= TNL_F_IPSEC;
+    } else if (!strcmp(type, "vxlan") || !strcmp(type, "ipsec_vxlan")) {
+        is_vxlan = true;
     }
 
     SMAP_FOR_EACH (node, args) {
@@ -638,6 +649,8 @@ parse_tunnel_config(const char *name, const char *type,
             } else {
                 nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
             }
+        } else if (!strcmp(node->key, "dst_port") && is_vxlan) {
+            nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, atoi(node->value));
         } else if (!strcmp(node->key, "csum") && is_gre) {
             if (!strcmp(node->value, "true")) {
                 flags |= TNL_F_CSUM;
@@ -756,6 +769,7 @@ tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
         [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
         [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
         [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
+        [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true },
     };
     struct ofpbuf buf;
 
@@ -835,6 +849,11 @@ unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
         smap_add_format(args, "tos", "0x%x", tos);
     }
 
+    if (a[OVS_TUNNEL_ATTR_DST_PORT]) {
+        ovs_be16 dst_port = nl_attr_get_be16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+        smap_add_format(args, "dst_port", "%d", dst_port);
+    }
+
     if (flags & TNL_F_CSUM) {
         smap_add(args, "csum", "true");
     }
@@ -990,6 +1009,14 @@ netdev_vport_register(void)
           { "capwap", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
           parse_tunnel_config, unparse_tunnel_config },
 
+        { OVS_VPORT_TYPE_VXLAN,
+          { "vxlan", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+          parse_tunnel_config, unparse_tunnel_config },
+
+        { OVS_VPORT_TYPE_VXLAN,
+          { "vxlan_ipsec", VPORT_FUNCTIONS(netdev_vport_get_drv_info) },
+          parse_tunnel_config, unparse_tunnel_config },
+
         { OVS_VPORT_TYPE_PATCH,
           { "patch", VPORT_FUNCTIONS(NULL) },
           parse_patch_config, unparse_patch_config }
diff --git a/rhel/etc_init.d_openvswitch b/rhel/etc_init.d_openvswitch
index af332c0..1064c4d 100755
--- a/rhel/etc_init.d_openvswitch
+++ b/rhel/etc_init.d_openvswitch
@@ -51,7 +51,9 @@ start () {
     set "$@" $OVS_CTL_OPTS
     "$@"
 
+    # Allow tunnel traffic.
     $ovs_ctl --protocol=gre enable-protocol
+    $ovs_ctl --protocol=udp --dport=4563 enable-protocol
 
     touch /var/lock/subsys/openvswitch
 }
diff --git a/tests/ovs-monitor-ipsec.at b/tests/ovs-monitor-ipsec.at
index e66c943..9fae50d 100644
--- a/tests/ovs-monitor-ipsec.at
+++ b/tests/ovs-monitor-ipsec.at
@@ -312,4 +312,71 @@ AT_CHECK([test ! -f etc/racoon/certs/ovs-3.4.5.6.pem])
 
 OVSDB_SERVER_SHUTDOWN
 
+###
+### Add an ipsec_vxlan psk interface and check what ovs-monitor-ipsec does
+###
+AT_CHECK([ovs_vsctl \
+              -- add-port br0 vxlan0 \
+              -- set interface vxlan0 type=ipsec_vxlan \
+                                      options:remote_ip=4.5.6.7 \
+                                      options:psk=mishmash])
+OVS_WAIT_UNTIL([test -f actions && grep 'spdadd 4.5.6.7' actions >/dev/null])
+AT_CHECK([sed '1,41d' actions], [0],
+[[racoon: reload
+setkey:
+> spdadd 0.0.0.0/0[4564] 4.5.6.7[4563] udp -P out ipsec esp/transport//require;
+> spdadd 4.5.6.7[4563] 0.0.0.0/0[4564] udp -P in ipsec esp/transport//require;
+]])
+AT_CHECK([trim etc/racoon/psk.txt], [0], [4.5.6.7   mishmash
+])
+AT_CHECK([trim etc/racoon/racoon.conf], [0], [dnl
+path pre_shared_key "/etc/racoon/psk.txt";
+path certificate "/etc/racoon/certs";
+remote 4.5.6.7 {
+        exchange_mode main;
+        nat_traversal on;
+        proposal {
+                encryption_algorithm aes;
+                hash_algorithm sha1;
+                authentication_method pre_shared_key;
+                dh_group 2;
+        }
+}
+sainfo anonymous {
+        pfs_group 2;
+        lifetime time 1 hour;
+        encryption_algorithm aes;
+        authentication_algorithm hmac_sha1, hmac_md5;
+        compression_algorithm deflate;
+}
+])
+
+###
+### Delete the ipsec_vxlan interface and check what ovs-monitor-ipsec does
+###
+AT_CHECK([ovs_vsctl del-port vxlan0])
+OVS_WAIT_UNTIL([test `wc -l < actions` -ge 17])
+AT_CHECK([sed '1,45d' actions], [0], [dnl
+racoon: reload
+setkey:
+> spddelete 0.0.0.0/0 4.5.6.7 udp -P out;
+> spddelete 4.5.6.7 0.0.0.0/0 udp -P in;
+setkey:
+> dump ;
+setkey:
+> dump ;
+])
+AT_CHECK([trim etc/racoon/psk.txt], [0], [])
+AT_CHECK([trim etc/racoon/racoon.conf], [0], [dnl
+path pre_shared_key "/etc/racoon/psk.txt";
+path certificate "/etc/racoon/certs";
+sainfo anonymous {
+        pfs_group 2;
+        lifetime time 1 hour;
+        encryption_algorithm aes;
+        authentication_algorithm hmac_sha1, hmac_md5;
+        compression_algorithm deflate;
+}
+])
+
 AT_CLEANUP
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 602e687..784bb04 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1200,8 +1200,7 @@
           <dt><code>gre</code></dt>
           <dd>
             An Ethernet over RFC 2890 Generic Routing Encapsulation over IPv4
-            tunnel.  See <ref group="Tunnel Options"/> for information on
-            configuring GRE tunnels.
+            tunnel.
           </dd>
 
           <dt><code>ipsec_gre</code></dt>
@@ -1238,6 +1237,32 @@
             February 2013.
           </dd>
 
+          <dt><code>vxlan</code></dt>
+          <dd>
+	    <p>
+	      An Ethernet tunnel over the experimental, UDP-based VXLAN
+	      protocol described at
+	      <code>http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02</code>.
+	      VXLAN is currently supported only with the Linux kernel datapath
+	      with kernel version 2.6.26 or later.
+	    </p>
+	    <p>
+	      As an experimental protocol, VXLAN has no officially assigned UDP
+	      port.  Open vSwitch currently uses UDP destination port 4563.
+	      The source port used for VXLAN traffic varies on a per-flow basis
+	      between 32768 and 65535 to allow load balancing.
+	    </p>
+          </dd>
+
+	  <dt><code>ipsec_vxlan</code></dt>
+	  <dd>
+	    VXLAN over an IPSEC tunnel.  As an exception to the usual rule that
+	    only one tunnel with the same matching criteria is allowed at a
+	    time, it is possible to have one <code>vxlan</code> tunnel and one
+	    <code>ipsec_vxlan</code> tunnel at the same time that are otherwise
+	    equivalent.
+	  </dd>
+
           <dt><code>patch</code></dt>
           <dd>
             A pair of virtual devices that act as a patch cable.
@@ -1254,7 +1279,8 @@
       <p>
         These options apply to interfaces with <ref column="type"/> of
         <code>gre</code>, <code>ipsec_gre</code>, <code>gre64</code>,
-        <code>ipsec_gre64</code>, and <code>capwap</code>.
+        <code>ipsec_gre64</code>, <code>capwap</code>.
+        <code>vxlan</code>, and <code>vxlan_ipsec</code>.
       </p>
 
       <p>
@@ -1297,8 +1323,9 @@
             key="in_key"/> at all.
           </li>
           <li>
-            A positive 32-bit (for GRE) or 64-bit (for CAPWAP) number.  The
-            tunnel receives only packets with the specified key.
+            A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
+            CAPWAP) number.  The tunnel receives only packets with the
+            specified key.
           </li>
           <li>
             The word <code>flow</code>.  The tunnel accepts packets with any
@@ -1323,8 +1350,9 @@
             key="out_key"/> at all.
           </li>
           <li>
-            A positive 32-bit (for GRE) or 64-bit (for CAPWAP) number.  Packets
-            sent through the tunnel will have the specified key.
+            A positive 24-bit (for VXLAN), 32-bit (for GRE) or 64-bit (for
+            CAPWAP) number.  Packets sent through the tunnel will have the
+            specified key.
           </li>
           <li>
             The word <code>flow</code>.  Packets sent through the tunnel will
@@ -1384,9 +1412,10 @@
         deprecated and will be removed soon.
       </column>
 
-      <group title="Tunnel Options: gre only">
+      <group title="Tunnel Options: gre and vxlan only">
         <p>
-          Only <code>gre</code> interfaces support these options.
+          Only <code>gre</code> and <code>vxlan</code> interfaces support these
+          options.
         </p>
       </group>
 
@@ -1418,11 +1447,19 @@
         </column>
       </group>
 
-      <group title="Tunnel Options: ipsec_gre only">
+      <group title="Tunnel Options: ipsec_gre and ipsec_vxlan only">
         <p>
-          Only <code>ipsec_gre</code> interfaces support these options.
+          Only <code>ipsec_gre</code> and <code>ipsec_vxlan</code> interfaces
+          support these options.
         </p>
 
+	<p>
+	  These options are implemented through a separate daemon named
+	  <code>ovs-monitor-ipsec</code> that so far has only been ported to
+	  and packaged for Debian (including derivative distributions such as
+	  Ubuntu).
+	</p>
+
         <column name="options" key="peer_cert">
           Required for certificate authentication.  A string containing the
           peer's certificate in PEM format.  Additionally the host's
diff --git a/xenserver/etc_init.d_openvswitch b/xenserver/etc_init.d_openvswitch
index 534451b..1d0567b 100755
--- a/xenserver/etc_init.d_openvswitch
+++ b/xenserver/etc_init.d_openvswitch
@@ -81,7 +81,9 @@ start () {
 
     start_ovs_xapi_sync
 
+    # Allow tunnel traffic.
     $ovs_ctl --protocol=gre enable-protocol
+    $ovs_ctl --protocol=udp --dport=4341 enable-protocol
 
     touch /var/lock/subsys/openvswitch
 }
-- 
1.7.11.7




More information about the dev mailing list