From b54bdbe993b89829aa33b7a207c61274b953faa5 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 23 Sep 2011 14:21:19 -0700 Subject: [PATCH] ovs-monitor-ipsec: Add unit test. --- debian/ovs-monitor-ipsec | 57 ++++++---- tests/automake.mk | 1 + tests/ovs-monitor-ipsec.at | 222 +++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 1 + 4 files changed, 258 insertions(+), 23 deletions(-) create mode 100644 tests/ovs-monitor-ipsec.at diff --git a/debian/ovs-monitor-ipsec b/debian/ovs-monitor-ipsec index 10b278d5..5aca0f7c 100755 --- a/debian/ovs-monitor-ipsec +++ b/debian/ovs-monitor-ipsec @@ -53,7 +53,9 @@ try: except socket.error, e: logging.basicConfig() s_log.warn("failed to connect to syslog (%s)" % e) +s_log.addHandler(logging.StreamHandler()) +root_prefix = '' # Prefix for absolute file names, for testing. setkey = "/usr/sbin/setkey" @@ -123,11 +125,12 @@ path certificate "%s"; self.psk_hosts = {} self.cert_hosts = {} - if not os.path.isdir(self.cert_dir): + if not os.path.isdir(root_prefix + self.cert_dir): os.mkdir(self.cert_dir) # Clean out stale peer certs from previous runs - for ovs_cert in glob.glob("%s/ovs-*.pem" % self.cert_dir): + for ovs_cert in glob.glob("%s%s/ovs-*.pem" + % (root_prefix, self.cert_dir)): try: os.remove(ovs_cert) except OSError: @@ -137,20 +140,22 @@ path certificate "%s"; self.commit() def reload(self): - exitcode = subprocess.call(["/etc/init.d/racoon", "reload"]) + exitcode = subprocess.call([root_prefix + "/etc/init.d/racoon", + "reload"]) if exitcode != 0: # Racoon is finicky about its configuration file and will # refuse to start if it sees something it doesn't like # (e.g., a certificate file doesn't exist). Try restarting # the process before giving up. s_log.warning("attempting to restart racoon") - exitcode = subprocess.call(["/etc/init.d/racoon", "restart"]) + exitcode = subprocess.call([root_prefix + "/etc/init.d/racoon", + "restart"]) if exitcode != 0: s_log.warning("couldn't reload racoon") def commit(self): # Rewrite the Racoon configuration file - conf_file = open(self.conf_file, 'w') + conf_file = open(root_prefix + self.conf_file, 'w') conf_file.write(Racoon.conf_header % (self.psk_file, self.cert_dir)) for host, vals in self.cert_hosts.iteritems(): @@ -165,7 +170,7 @@ path certificate "%s"; # Rewrite the pre-shared keys file; it must only be readable by root. orig_umask = os.umask(0077) - psk_file = open(Racoon.psk_file, 'w') + psk_file = open(root_prefix + Racoon.psk_file, 'w') os.umask(orig_umask) psk_file.write("# Generated by Open vSwitch...do not modify by hand!") @@ -186,10 +191,10 @@ path certificate "%s"; def _verify_certs(self, vals): # Racoon will refuse to start if the certificate files don't # exist, so verify that they're there. - if not os.path.isfile(vals["certificate"]): + if not os.path.isfile(root_prefix + vals["certificate"]): raise error.Error("'certificate' file does not exist: %s" % vals["certificate"]) - elif not os.path.isfile(vals["private_key"]): + elif not os.path.isfile(root_prefix + vals["private_key"]): raise error.Error("'private_key' file does not exist: %s" % vals["private_key"]) @@ -199,11 +204,11 @@ path certificate "%s"; if vals["peer_cert"].find("-----BEGIN CERTIFICATE-----") == -1: raise error.Error("'peer_cert' is not in valid PEM format") - cert = open(vals["certificate"]).read() + cert = open(root_prefix + vals["certificate"]).read() if cert.find("-----BEGIN CERTIFICATE-----") == -1: raise error.Error("'certificate' is not in valid PEM format") - cert = open(vals["private_key"]).read() + cert = open(root_prefix + vals["private_key"]).read() if cert.find("-----BEGIN RSA PRIVATE KEY-----") == -1: raise error.Error("'private_key' is not in valid PEM format") @@ -227,7 +232,7 @@ path certificate "%s"; # The peer's certificate comes to us in PEM format as a string. # Write that string to a file for Racoon to use. peer_cert_file = "%s/ovs-%s.pem" % (self.cert_dir, host) - f = open(peer_cert_file, "w") + f = open(root_prefix + peer_cert_file, "w") f.write(vals["peer_cert"]) f.close() @@ -241,7 +246,7 @@ path certificate "%s"; del self.cert_hosts[host] self.commit() try: - os.remove(peer_cert_file) + os.remove(root_prefix + peer_cert_file) except OSError: pass @@ -271,10 +276,11 @@ class IPsec: def call_setkey(self, cmds): try: - p = subprocess.Popen([setkey, "-c"], stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + p = subprocess.Popen([root_prefix + setkey, "-c"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) except: - s_log.error("could not call setkey") + s_log.error("could not call %s%s" % (root_prefix, setkey)) sys.exit(1) # xxx It is safer to pass the string into the communicate() @@ -290,7 +296,7 @@ class IPsec: # older entry could be in a "dying" state. spi_list = [] host_line = "%s %s" % (local_ip, remote_ip) - results = self.call_setkey("dump ;").split("\n") + results = self.call_setkey("dump ;\n").split("\n") for i in range(len(results)): if results[i].strip() == host_line: # The SPI is in the line following the host pair @@ -301,7 +307,7 @@ class IPsec: return spi_list def sad_flush(self): - self.call_setkey("flush;") + self.call_setkey("flush;\n") def sad_del(self, local_ip, remote_ip): # To delete all SAD entries, we should be able to use setkey's @@ -323,18 +329,18 @@ class IPsec: self.call_setkey(cmds) def spd_flush(self): - self.call_setkey("spdflush;") + 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;" % + 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;" % (remote_ip, local_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): @@ -403,8 +409,10 @@ def usage(): print "usage: %s [OPTIONS] DATABASE" % sys.argv[0] print "where DATABASE is a socket on which ovsdb-server is listening." ovs.daemon.usage() - print "Other options:" - print " -h, --help display this help message" + print """\ +Other options: + --root-prefix=DIR Use DIR as alternate root directory (for testing). + -h, --help Display this help message.""" sys.exit(0) @@ -441,7 +449,7 @@ def get_ssl_cert(data): def main(argv): try: options, args = getopt.gnu_getopt( - argv[1:], 'h', ['help'] + ovs.daemon.LONG_OPTIONS) + argv[1:], 'h', ['help', 'root-prefix='] + ovs.daemon.LONG_OPTIONS) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg)) sys.exit(1) @@ -449,6 +457,9 @@ def main(argv): for key, value in options: if key in ['-h', '--help']: usage() + elif key == "--root-prefix": + global root_prefix + root_prefix = value elif not ovs.daemon.parse_opt(key, value): sys.stderr.write("%s: unhandled option %s\n" % (ovs.util.PROGRAM_NAME, key)) diff --git a/tests/automake.mk b/tests/automake.mk index 9b094de1..dcf6026e 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -51,6 +51,7 @@ TESTSUITE_AT = \ tests/ovsdb-monitor.at \ tests/ovsdb-idl.at \ tests/ovs-vsctl.at \ + tests/ovs-monitor-ipsec.at \ tests/interface-reconfigure.at TESTSUITE = $(srcdir)/tests/testsuite DISTCLEANFILES += tests/atconfig tests/atlocal diff --git a/tests/ovs-monitor-ipsec.at b/tests/ovs-monitor-ipsec.at new file mode 100644 index 00000000..ad1e96e7 --- /dev/null +++ b/tests/ovs-monitor-ipsec.at @@ -0,0 +1,222 @@ +AT_BANNER([ovs-monitor-ipsec]) + +AT_SETUP([ovs-monitor-ipsec]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) + +OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR +cp "$top_srcdir/vswitchd/vswitch.ovsschema" . + +trap 'kill `cat pid ovs-monitor-ipsec.pid`' 0 + +mkdir etc etc/init.d etc/racoon etc/racoon/certs +mkdir usr usr/sbin + +AT_DATA([etc/init.d/racoon], [dnl +#! /bin/sh +echo "racoon: $@" >&3 +exit 0 +]) +chmod +x etc/init.d/racoon + +AT_DATA([usr/sbin/setkey], [dnl +#! /bin/sh +exec >&3 +echo "setkey:" +while read line; do + echo "> $line" +done +]) +chmod +x usr/sbin/setkey + +touch etc/racoon/certs/ovs-stale.pem + +ovs_vsctl () { + ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket "$@" +} +trim () { # Removes blank lines and lines starting with # from input. + sed -e '/^#/d' -e '/^[ ]*$/d' "$@" +} + +### +### Start ovsdb-server. +### +OVS_VSCTL_SETUP + +### +### Start ovs-monitor-ipsec and wait for it to delete the stale cert. +### +AT_CHECK( + [$PYTHON $top_srcdir/debian/ovs-monitor-ipsec "--root-prefix=`pwd`" \ + "--pidfile-name=`pwd`/ovs-monitor-ipsec.pid" \ + unix:socket 2>log 3>actions &]) +AT_CAPTURE_FILE([log]) +AT_CAPTURE_FILE([actions]) +OVS_WAIT_UNTIL([test ! -f etc/racoon/certs/ovs-stale.pem]) + +### +### Add an ipsec_gre psk interface and check what ovs-monitor-ipsec does +### +AT_CHECK([ovs_vsctl \ + -- add-br br0 \ + -- add-port br0 gre0 \ + -- set interface gre0 type=ipsec_gre \ + options:remote_ip=1.2.3.4 \ + options:psk=swordfish]) +OVS_WAIT_UNTIL([test -f actions && grep 'spdadd 1.2.3.4' actions >/dev/null]) +AT_CHECK([cat actions], [0], [dnl +setkey: +> flush; +setkey: +> spdflush; +racoon: reload +racoon: reload +setkey: +> spdadd 0.0.0.0/0 1.2.3.4 gre -P out ipsec esp/transport//require; +> spdadd 1.2.3.4 0.0.0.0/0 gre -P in ipsec esp/transport//require; +]) +AT_CHECK([trim etc/racoon/psk.txt], [0], [1.2.3.4 swordfish +]) +AT_CHECK([trim etc/racoon/racoon.conf], [0], [dnl +path pre_shared_key "/etc/racoon/psk.txt"; +path certificate "/etc/racoon/certs"; +remote 1.2.3.4 { + 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_gre interface and check what ovs-monitor-ipsec does +### +AT_CHECK([ovs_vsctl del-port gre0]) +OVS_WAIT_UNTIL([test `wc -l < actions` -ge 17]) +AT_CHECK([sed '1,9d' actions], [0], [dnl +racoon: reload +setkey: +> spddelete 0.0.0.0/0 1.2.3.4 gre -P out; +> spddelete 1.2.3.4 0.0.0.0/0 gre -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; +} +]) + +### +### Add ipsec_gre certificate interface and check what ovs-monitor-ipsec does +### +AT_DATA([cert.pem], [dnl +-----BEGIN CERTIFICATE----- +(not a real certificate) +-----END CERTIFICATE----- +]) +AT_DATA([key.pem], [dnl +-----BEGIN RSA PRIVATE KEY----- +(not a real private key) +-----END RSA PRIVATE KEY----- +]) +AT_CHECK([ovs_vsctl \ + -- add-port br0 gre1 \ + -- set Interface gre1 type=ipsec_gre \ + options:remote_ip=2.3.4.5 \ + options:peer_cert='"-----BEGIN CERTIFICATE----- +(not a real peer certificate) +-----END CERTIFICATE----- +"' \ + options:certificate='"/cert.pem"' \ + options:private_key='"/key.pem"']) +OVS_WAIT_UNTIL([test `wc -l < actions` -ge 21]) +AT_CHECK([sed '1,17d' actions], [0], [dnl +racoon: reload +setkey: +> spdadd 0.0.0.0/0 2.3.4.5 gre -P out ipsec esp/transport//require; +> spdadd 2.3.4.5 0.0.0.0/0 gre -P in ipsec esp/transport//require; +]) +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"; +remote 2.3.4.5 { + exchange_mode main; + nat_traversal on; + ike_frag on; + certificate_type x509 "/cert.pem" "/key.pem"; + my_identifier asn1dn; + peers_identifier asn1dn; + peers_certfile x509 "/etc/racoon/certs/ovs-2.3.4.5.pem"; + verify_identifier on; + proposal { + encryption_algorithm aes; + hash_algorithm sha1; + authentication_method rsasig; + dh_group 2; + } +} +sainfo anonymous { + pfs_group 2; + lifetime time 1 hour; + encryption_algorithm aes; + authentication_algorithm hmac_sha1, hmac_md5; + compression_algorithm deflate; +} +]) +AT_CHECK([cat etc/racoon/certs/ovs-2.3.4.5.pem], [0], [dnl +-----BEGIN CERTIFICATE----- +(not a real peer certificate) +-----END CERTIFICATE----- +]) + +### +### Delete the ipsec_gre certificate interface. +### +AT_CHECK([ovs_vsctl del-port gre1]) +OVS_WAIT_UNTIL([test `wc -l < actions` -ge 29]) +AT_CHECK([sed '1,21d' actions], [0], [dnl +racoon: reload +setkey: +> spddelete 0.0.0.0/0 2.3.4.5 gre -P out; +> spddelete 2.3.4.5 0.0.0.0/0 gre -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_CHECK([test ! -f etc/racoon/certs/ovs-2.3.4.5.pem]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index ea5208f4..19b7802d 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -64,4 +64,5 @@ m4_include([tests/ofproto.at]) m4_include([tests/ofproto-dpif.at]) m4_include([tests/ovsdb.at]) m4_include([tests/ovs-vsctl.at]) +m4_include([tests/ovs-monitor-ipsec.at]) m4_include([tests/interface-reconfigure.at]) -- 2.30.2