From: Justin Pettit <[email protected]> This makes it possible to automatically set up IPsec encrypted VXLAN tunnels through the OVS database.
Signed-off-by: Justin Pettit <[email protected]> Signed-off-by: Jesse Gross <[email protected]> [Ben modified this commit for VXLAN, added unit tests, and tweaked it] Signed-off-by: Ben Pfaff <[email protected]> --- NEWS | 3 +- README | 2 +- datapath/vport-vxlan.c | 4 ++ debian/control | 4 +- debian/openvswitch-ipsec.init | 3 +- debian/ovs-monitor-ipsec | 117 ++++++++++++++++++++++++++--------------- lib/netdev-vport.c | 48 +++++++++++------ tests/ovs-monitor-ipsec.at | 67 +++++++++++++++++++++++ vswitchd/vswitch.xml | 20 ++++++- 9 files changed, 200 insertions(+), 68 deletions(-) diff --git a/NEWS b/NEWS index 27bb260..540dbff 100644 --- a/NEWS +++ b/NEWS @@ -2,7 +2,8 @@ Post-v1.2.0 ------------------------ - New support for the experimental VXLAN tunnel protocol (see - http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-00). + http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-00) + and VXLAN over IPSEC. - OpenFlow: - Added an OpenFlow extension which allows the "output" action to accept NXM fields. diff --git a/README b/README index 434bf13..bb16a3c 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), SPAN, RSPAN, and ERSPAN for increased visibility * QoS (Quality of Service) configuration, plus policing - * GRE, GRE over IPSEC, CAPWAP, and VXLAN tunneling + * GRE, GRE over IPSEC, CAPWAP, and 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/vport-vxlan.c b/datapath/vport-vxlan.c index 6ff87e8..b839c4c 100644 --- a/datapath/vport-vxlan.c +++ b/datapath/vport-vxlan.c @@ -24,6 +24,7 @@ #include "vport-generic.h" #define VXLAN_DST_PORT 4563 +#define VXLAN_IPSEC_SRC_PORT 4564 #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ @@ -55,6 +56,9 @@ static int vxlan_hdr_len(const struct tnl_mutable_config *mutable) static __be16 get_src_port(const struct sk_buff *skb, const struct tnl_mutable_config *mutable) { + if (mutable->flags & TNL_F_IPSEC) + return htons(VXLAN_IPSEC_SRC_PORT); + /* Convert hash into a port between 32768 and 65535. */ return (__force __be16)OVS_CB(skb)->flow->hash | htons(32768); } diff --git a/debian/control b/debian/control index 1f3387a..4c23e59 100644 --- a/debian/control +++ b/debian/control @@ -60,9 +60,9 @@ Depends: openvswitch-common (= ${binary:Version}), openvswitch-switch (= ${binary:Version}), python-openvswitch (= ${source:Version}) -Description: Open vSwitch GRE-over-IPsec support +Description: Open vSwitch support for GRE and VXLAN over ISPEC The ovs-monitor-ipsec script provides support for encrypting GRE - tunnels with IPsec. + and VXLAN tunnels with IPsec. . Open vSwitch is a full-featured software-based Ethernet switch. diff --git a/debian/openvswitch-ipsec.init b/debian/openvswitch-ipsec.init index 17835a5..bb9a5bd 100755 --- a/debian/openvswitch-ipsec.init +++ b/debian/openvswitch-ipsec.init @@ -1,5 +1,6 @@ #!/bin/sh # +# Copyright (c) 2011 Nicira Networks # Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <[email protected]> # # This is free software; you may redistribute it and/or modify @@ -23,7 +24,7 @@ # 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 ### 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 ac2cd7e..48741f5 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 @@ -41,7 +42,12 @@ import ovs.vlog vlog = ovs.vlog.Vlog("ovs-monitor-ipsec") root_prefix = '' # Prefix for absolute file names, for testing. -setkey = "/usr/sbin/setkey" +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 # Class to configure the racoon daemon, which handles IKE negotiation @@ -251,17 +257,17 @@ 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() self.spd_flush() self.racoon = Racoon() - self.entries = [] + self.entries = {} def call_setkey(self, cmds): try: - p = subprocess.Popen([root_prefix + setkey, "-c"], + p = subprocess.Popen([root_prefix + SETKEY, "-c"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) except: @@ -317,26 +323,44 @@ 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[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)) + 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: @@ -344,7 +368,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 keep_table_columns(schema, table_name, column_types): @@ -463,36 +487,43 @@ def main(): new_interfaces = {} for rec in idl.tables["Interface"].rows.itervalues(): if rec.type == "ipsec_gre": - name = rec.name - options = rec.options - 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"), - "psk": options.get("psk")} - - 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 + tunnel_type = "gre" + elif rec.type == "ipsec_vxlan": + tunnel_type = "vxlan" + else: + continue + + name = rec.name + options = rec.options + 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"), + "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 - # 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/lib/netdev-vport.c b/lib/netdev-vport.c index 9db93ba..d2c9e14 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -135,11 +135,27 @@ netdev_vport_get_vport_type(const struct netdev *netdev) : OVS_VPORT_TYPE_UNSPEC); } -const char * -netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport) +static const char * +get_maybe_ipsec_tunnel_type(const struct dpif_linux_vport *vport, + const char *plain_type, const char *ipsec_type) { struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]; + uint32_t flags; + + if (tnl_port_config_from_nlattr(vport->options, vport->options_len, a)) { + VLOG_WARN_RL(&rl, "dp%d: cannot parse options for port `%s' (type %u)", + vport->dp_ifindex, vport->name, + (unsigned int) vport->type); + return "unknown"; + } + + flags = nl_attr_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]); + return flags & TNL_F_IPSEC ? ipsec_type : plain_type; +} +const char * +netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport) +{ switch (vport->type) { case OVS_VPORT_TYPE_UNSPEC: break; @@ -154,18 +170,13 @@ netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport) return "patch"; case OVS_VPORT_TYPE_GRE: - 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_gre" : "gre"); + return get_maybe_ipsec_tunnel_type(vport, "gre", "ipsec_gre"); case OVS_VPORT_TYPE_CAPWAP: return "capwap"; case OVS_VPORT_TYPE_VXLAN: - return "vxlan"; + return get_maybe_ipsec_tunnel_type(vport, "vxlan", "ipsec_vxlan"); case __OVS_VPORT_TYPE_MAX: break; @@ -576,19 +587,18 @@ static int parse_tunnel_config(const char *name, const char *type, const struct shash *args, struct ofpbuf *options) { - bool is_gre = false; - bool is_ipsec = false; + bool supports_csum; + bool is_ipsec; struct shash_node *node; bool ipsec_mech_set = false; ovs_be32 daddr = htonl(0); uint32_t flags; + supports_csum = !strcmp(type, "gre") || !strcmp(type, "ipsec_gre"); + is_ipsec = !strncmp(type, "ipsec_", 6); + flags = TNL_F_DF_DEFAULT | TNL_F_PMTUD | TNL_F_HDR_CACHE; - if (!strcmp(type, "gre")) { - is_gre = true; - } else if (!strcmp(type, "ipsec_gre")) { - is_gre = true; - is_ipsec = true; + if (is_ipsec) { flags |= TNL_F_IPSEC; flags &= ~TNL_F_HDR_CACHE; } @@ -621,7 +631,7 @@ parse_tunnel_config(const char *name, const char *type, } else { nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->data)); } - } else if (!strcmp(node->name, "csum") && is_gre) { + } else if (!strcmp(node->name, "csum") && supports_csum) { if (!strcmp(node->data, "true")) { flags |= TNL_F_CSUM; } @@ -962,6 +972,10 @@ netdev_vport_register(void) { "vxlan", VPORT_FUNCTIONS(netdev_vport_get_status) }, parse_tunnel_config, unparse_tunnel_config }, + { OVS_VPORT_TYPE_VXLAN, + { "vxlan_ipsec", VPORT_FUNCTIONS(netdev_vport_get_status) }, + parse_tunnel_config, unparse_tunnel_config }, + { OVS_VPORT_TYPE_PATCH, { "patch", VPORT_FUNCTIONS(NULL) }, parse_patch_config, unparse_patch_config } diff --git a/tests/ovs-monitor-ipsec.at b/tests/ovs-monitor-ipsec.at index f9868e7..2b37b85 100644 --- a/tests/ovs-monitor-ipsec.at +++ b/tests/ovs-monitor-ipsec.at @@ -308,4 +308,71 @@ sainfo anonymous { ]) AT_CHECK([test ! -f etc/racoon/certs/ovs-3.4.5.6.pem]) +### +### 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[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; +]]) +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 dbbfef4..6f2e73a 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -951,6 +951,12 @@ between 32768 and 65535 to allow load balancing. </p> </dd> + + <dt><code>ipsec_vxlan</code></dt> + <dd> + VXLAN over an IPSEC tunnel. + </dd> + <dt><code>patch</code></dt> <dd> A pair of virtual devices that act as a patch cable. @@ -966,7 +972,7 @@ <p> These options apply to interfaces with <ref column="type"/> of <code>gre</code>, <code>ipsec_gre</code>, <code>capwap</code>, - and <code>vxlan</code>. + <code>vxlan</code>, and <code>vxlan_ipsec</code>. </p> <p> @@ -1131,11 +1137,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 -- 1.7.4.4 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
