[ovs-dev] [PATCH 5/5] stt: Support simultaneous encrypted and unencrypted to same host

Ben Pfaff blp at nicira.com
Wed Oct 19 00:02:01 UTC 2011


From: Justin Pettit <jpettit at nicira.com>

In general it is only possible to have a single tunnel between a pair
of end hosts with a given key.  This allows one encrypted and one
unencrypted tunnel when using STT.  They are distinguished by using
a unique port number on transmit and looking at the security path
on receive.

Signed-off-by: Justin Pettit <jpettit at nicira.com>
Signed-off-by: Jesse Gross <jesse at nicira.com>
[Ben modified this patch for VXLAN, fixed >=2.6.39, updated unit test]
Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 datapath/tunnel.c          |    9 ++++++-
 datapath/tunnel.h          |   10 +++++++++
 datapath/vport-capwap.c    |    2 +
 datapath/vport-vxlan.c     |   46 ++++++++++++++++++++++++++++++++++++++++++-
 debian/ovs-monitor-ipsec   |   10 +++-----
 tests/ovs-monitor-ipsec.at |    4 +-
 vswitchd/vswitch.xml       |    6 ++++-
 7 files changed, 74 insertions(+), 13 deletions(-)

diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index 8edff06..ca4f337 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -929,7 +929,10 @@ static struct rtable *find_route(struct vport *vport,
 					      { .daddr = mutable->key.daddr,
 						.saddr = mutable->key.saddr,
 						.tos = tos } },
-				    .proto = tnl_vport->tnl_ops->ipproto };
+				    .proto = tnl_vport->tnl_ops->ipproto,
+				    .uli_u = { .ports =
+					      { .sport = tnl_vport->tnl_ops->sport,
+						.dport = tnl_vport->tnl_ops->dport } } };
 
 		if (unlikely(ip_route_output_key(&init_net, &rt, &fl)))
 			return NULL;
@@ -937,7 +940,9 @@ static struct rtable *find_route(struct vport *vport,
 		struct flowi4 fl = { .daddr = mutable->key.daddr,
 				     .saddr = mutable->key.saddr,
 				     .flowi4_tos = tos,
-				     .flowi4_proto = tnl_vport->tnl_ops->ipproto };
+				     .flowi4_proto = tnl_vport->tnl_ops->ipproto,
+				     .fl4_sport = tnl_vport->tnl_ops->sport,
+				     .fl4_dport = tnl_vport->tnl_ops->dport };
 
 		rt = ip_route_output_key(&init_net, &fl);
 		if (IS_ERR(rt))
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index e1d5489..b09220f 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -34,6 +34,7 @@
 /* These flags are only needed when calling tnl_find_port(). */
 #define TNL_T_KEY_EXACT		(1 << 10)
 #define TNL_T_KEY_MATCH		(1 << 11)
+#define TNL_T_IPSEC		(1 << 12)
 
 /* Private flags not exposed to userspace in this form. */
 #define TNL_F_IN_KEY_MATCH      (1 << 16) /* Store the key in tun_id to match in flow table. */
@@ -97,6 +98,15 @@ struct tnl_ops {
 	u8 ipproto;		/* The IP protocol for the tunnel. */
 
 	/*
+	 * Source and destination port for the tunnel.  This is necessary
+	 * if the tunnel protocol is using TCP or UDP within IPsec.  The
+	 * IPsec transformation is done as part of the route lookup, so
+	 * setting the transport port must be done early.
+	 */
+	__be16 sport;
+	__be16 dport;
+
+	/*
 	 * Returns the length of the tunnel header that will be added in
 	 * build_header() (i.e. excludes the IP header).  Returns a negative
 	 * error code if the configuration is invalid.
diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c
index 3fb4ffb..3668f53 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -358,6 +358,8 @@ out:
 static const struct tnl_ops capwap_tnl_ops = {
 	.tunnel_type	= TNL_T_PROTO_CAPWAP,
 	.ipproto	= IPPROTO_UDP,
+	.sport		= htons(CAPWAP_SRC_PORT),
+	.dport		= htons(CAPWAP_DST_PORT),
 	.hdr_len	= capwap_hdr_len,
 	.build_header	= capwap_build_header,
 	.update_header	= capwap_update_header,
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index b839c4c..8e1a091 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -15,9 +15,11 @@
 #include <linux/ip.h>
 #include <linux/net.h>
 #include <linux/udp.h>
+#include <linux/xfrm.h>
 
 #include <net/icmp.h>
 #include <net/ip.h>
+#include <net/xfrm.h>
 
 #include "tunnel.h"
 #include "vport.h"
@@ -102,6 +104,21 @@ static struct sk_buff *vxlan_update_header(const struct vport *vport,
 	return skb;
 }
 
+static bool sec_path_esp(struct sk_buff *skb)
+{
+	struct sec_path *sp = skb_sec_path(skb);
+
+	if (sp) {
+		int i;
+
+		for (i = 0; i < sp->len; i++)
+			if (sp->xvec[i]->id.proto == XFRM_PROTO_ESP)
+				return true;
+	}
+
+	return false;
+}
+
 /* Called with rcu_read_lock and BH disabled. */
 static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 {
@@ -109,6 +126,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	struct vxlanhdr *vxh;
 	const struct tnl_mutable_config *mutable;
 	struct iphdr *iph;
+	int tunnel_type;
 	__be64 key;
 
 	if (unlikely(!pskb_may_pull(skb, VXLAN_HLEN + ETH_HLEN)))
@@ -123,8 +141,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 
 	key = cpu_to_be64(ntohl(vxh->vx_vni) >> 8);
 
+	tunnel_type = TNL_T_PROTO_VXLAN;
+	if (sec_path_esp(skb))
+		tunnel_type |= TNL_T_IPSEC;
+
 	iph = ip_hdr(skb);
-	vport = tnl_find_port(iph->daddr, iph->saddr, key, TNL_T_PROTO_VXLAN,
+	vport = tnl_find_port(iph->daddr, iph->saddr, key, tunnel_type,
 			      &mutable);
 	if (unlikely(!vport)) {
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
@@ -150,6 +172,17 @@ out:
 static const struct tnl_ops vxlan_tnl_ops = {
 	.tunnel_type	= TNL_T_PROTO_VXLAN,
 	.ipproto	= IPPROTO_UDP,
+	.dport		= htons(VXLAN_DST_PORT),
+	.hdr_len	= vxlan_hdr_len,
+	.build_header	= vxlan_build_header,
+	.update_header	= vxlan_update_header,
+};
+
+static const struct tnl_ops ipsec_vxlan_tnl_ops = {
+	.tunnel_type	= TNL_T_PROTO_VXLAN | TNL_T_IPSEC,
+	.ipproto	= IPPROTO_UDP,
+	.sport		= htons(VXLAN_IPSEC_SRC_PORT),
+	.dport		= htons(VXLAN_DST_PORT),
 	.hdr_len	= vxlan_hdr_len,
 	.build_header	= vxlan_build_header,
 	.update_header	= vxlan_update_header,
@@ -199,6 +232,7 @@ static void vxlan_uninit(void)
 
 static struct vport *vxlan_create(const struct vport_parms *parms)
 {
+	struct nlattr *flags_nlattr;
 	struct vport *vport;
 	int error;
 
@@ -206,7 +240,15 @@ static struct vport *vxlan_create(const struct vport_parms *parms)
 	if (error)
 		return ERR_PTR(error);
 
-	vport = tnl_create(parms, &vxlan_vport_ops, &vxlan_tnl_ops);
+	flags_nlattr = nla_find_nested(parms->options, OVS_TUNNEL_ATTR_FLAGS);
+	if (!flags_nlattr || nla_len(flags_nlattr) != sizeof(u32))
+		return ERR_PTR(-EINVAL);
+
+	if (nla_get_u32(flags_nlattr) & TNL_F_IPSEC)
+	    vport = tnl_create(parms, &vxlan_vport_ops, &ipsec_vxlan_tnl_ops);
+	else
+	    vport = tnl_create(parms, &vxlan_vport_ops, &vxlan_tnl_ops);
+
 	if (IS_ERR(vport))
 		vxlan_uninit();
 	return vport;
diff --git a/debian/ovs-monitor-ipsec b/debian/ovs-monitor-ipsec
index 48741f5..6c3c678 100755
--- a/debian/ovs-monitor-ipsec
+++ b/debian/ovs-monitor-ipsec
@@ -325,12 +325,10 @@ class IPsec:
     def spd_add(self, local_ip, remote_ip):
         tunnel_type = self.entries[remote_ip]
         if tunnel_type == "vxlan":
-            cmds = ("spdadd %s[any] %s[any] udp -P out ipsec esp/transport/%s[%s]-%s[%s]/require;\n"
-                        % (local_ip,  remote_ip, local_ip, VXLAN_SRC_PORT,
-                           remote_ip, VXLAN_DST_PORT))
-            cmds += ("spdadd %s[any] %s[any] udp -P in ipsec esp/transport/%s[%s]-%s[%s]/require;\n"
-                        % (remote_ip, local_ip, remote_ip, VXLAN_DST_PORT,
-                           local_ip, VXLAN_SRC_PORT))
+            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))
diff --git a/tests/ovs-monitor-ipsec.at b/tests/ovs-monitor-ipsec.at
index 2b37b85..14a4621 100644
--- a/tests/ovs-monitor-ipsec.at
+++ b/tests/ovs-monitor-ipsec.at
@@ -320,8 +320,8 @@ 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[any] 4.5.6.7[any] udp -P out ipsec esp/transport/0.0.0.0/0[4564]-4.5.6.7[4563]/require;
-> spdadd 4.5.6.7[any] 0.0.0.0/0[any] udp -P in ipsec esp/transport/4.5.6.7[4563]-0.0.0.0/0[4564]/require;
+> 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
 ])
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 6f2e73a..da223a3 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -954,7 +954,11 @@
 
 	  <dt><code>ipsec_vxlan</code></dt>
 	  <dd>
-	    VXLAN over an IPSEC tunnel.
+	    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>
-- 
1.7.4.4




More information about the dev mailing list