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
