From: Justin Pettit <[email protected]>

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 <[email protected]>
Signed-off-by: Jesse Gross <[email protected]>
[Ben modified this patch for VXLAN, fixed >=2.6.39, updated unit test]
Signed-off-by: Ben Pfaff <[email protected]>
---
 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

_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev

Reply via email to