Rename controller_connection to rconn and use it in secchan also.
[openvswitch] / secchan / secchan.c
index b8a4c4c307c51134a8802a607f056d7727a5ff6e..e24869363937bbaab4c2787ea4486a0e7e1140fe 100644 (file)
@@ -1,22 +1,34 @@
-/* Copyright (C) 2007 Board of Trustees, Leland Stanford Jr. University.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ * 
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
  */
 
 #include <errno.h>
@@ -24,6 +36,7 @@
 #include <poll.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #include "buffer.h"
 #include "compiler.h"
 #include "fault.h"
 #include "util.h"
-#include "vconn.h"
+#include "rconn.h"
+#include "vconn-ssl.h"
 #include "vlog-socket.h"
 #include "openflow.h"
+#include "poll-loop.h"
 
 #include "vlog.h"
 #define THIS_MODULE VLM_secchan
@@ -43,21 +58,15 @@ static void usage(void) NO_RETURN;
 
 static bool reliable = true;
 
-struct half {
-    const char *name;
-    struct vconn *vconn;
-    struct pollfd *pollfd;
-    struct buffer *rxbuf;
-};
-
-static void reconnect(struct half *);
-
 int
 main(int argc, char *argv[])
 {
+    struct half {
+        struct rconn *rconn;
+        struct buffer *rxbuf;
+    };
+
     struct half halves[2];
-    struct pollfd pollfds[2 + 1];
-    struct vlog_server *vlog_server;
     int retval;
     int i;
 
@@ -70,135 +79,76 @@ main(int argc, char *argv[])
         fatal(0, "exactly two peer arguments required; use --help for usage");
     }
 
-    retval = vlog_server_listen(NULL, &vlog_server);
+    retval = vlog_server_listen(NULL, NULL);
     if (retval) {
         fatal(retval, "Could not listen for vlog connections");
     }
 
     for (i = 0; i < 2; i++) {
-        halves[i].name = argv[optind + i];
-        halves[i].vconn = NULL;
-        halves[i].pollfd = &pollfds[i];
+        halves[i].rconn = rconn_new(argv[optind + i], 1);
         halves[i].rxbuf = NULL;
-        reconnect(&halves[i]);
     }
     for (;;) {
-        /* Wait until there's something to do. */
-        for (i = 0; i < 2; i++) {
-            struct half *this = &halves[i];
-            struct half *peer = &halves[!i];
-            int want = 0;
-            if (peer->rxbuf) {
-                want |= WANT_SEND;
-            }
-            if (!this->rxbuf) {
-                want |= WANT_RECV;
-            }
-            this->pollfd->fd = -1;
-            this->pollfd->events = 0;
-            vconn_prepoll(this->vconn, want, this->pollfd);
-        }
-        if (vlog_server) {
-            pollfds[2].fd = vlog_server_get_fd(vlog_server);
-            pollfds[2].events = POLLIN;
-        }
-        do {
-            retval = poll(pollfds, 2 + (vlog_server != NULL), -1);
-        } while (retval < 0 && errno == EINTR);
-        if (retval <= 0) {
-            fatal(retval < 0 ? errno : 0, "poll");
-        }
+        /* Do some work.  Limit the number of iterations so that callbacks
+         * registered with the poll loop don't starve. */
+        int iteration;
 
-        /* Let each connection deal with any pending operations. */
         for (i = 0; i < 2; i++) {
-            struct half *this = &halves[i];
-            vconn_postpoll(this->vconn, &this->pollfd->revents);
-            if (this->pollfd->revents & POLLERR) {
-                this->pollfd->revents |= POLLIN | POLLOUT;
-            }
-        }
-        if (vlog_server && pollfds[2].revents) {
-            vlog_server_poll(vlog_server);
+            rconn_run(halves[i].rconn);
         }
 
-        /* Do as much work as we can without waiting. */
-        for (i = 0; i < 2; i++) {
-            struct half *this = &halves[i];
-            struct half *peer = &halves[!i];
+        for (iteration = 0; iteration < 50; iteration++) {
+            bool progress = false;
+            for (i = 0; i < 2; i++) {
+                struct half *this = &halves[i];
+                struct half *peer = &halves[!i];
 
-            if (this->pollfd->revents & POLLIN && !this->rxbuf) {
-                retval = vconn_recv(this->vconn, &this->rxbuf);
-                if (retval && retval != EAGAIN) {
-                    VLOG_DBG("%s: recv: closing connection: %s",
-                             this->name, strerror(retval));
-                    reconnect(this);
-                    break;
+                if (!this->rxbuf) {
+                    this->rxbuf = rconn_recv(this->rconn);
                 }
-            }
 
-            if (peer->pollfd->revents & POLLOUT && this->rxbuf) {
-                retval = vconn_send(peer->vconn, this->rxbuf);
-                if (!retval) {
-                    this->rxbuf = NULL;
-                } else if (retval != EAGAIN) {
-                    VLOG_DBG("%s: send: closing connection: %s",
-                             peer->name, strerror(retval));
-                    reconnect(peer); 
-                    break;
+                if (this->rxbuf) {
+                    retval = rconn_send(peer->rconn, this->rxbuf);
+                    if (retval != EAGAIN) {
+                        this->rxbuf = NULL;
+                        if (!retval) {
+                            progress = true;
+                        }
+                    }
                 }
-            } 
-        }
-    }
-
-    return 0;
-}
-
-static void
-reconnect(struct half *this) 
-{
-    int backoff;
-    
-    if (this->vconn != NULL) {
-        if (!reliable) {
-            fatal(0, "%s: connection dropped", this->name);
+            }
+            if (!progress) {
+                break;
+            }
         }
 
-        VLOG_WARN("%s: connection dropped, reconnecting", this->name);
-        vconn_close(this->vconn);
-        this->vconn = NULL;
-        buffer_delete(this->rxbuf);
-        this->rxbuf = NULL;
-    }
-    this->pollfd->revents = POLLIN | POLLOUT;
+        /* Wait for something to happen. */
+        for (i = 0; i < 2; i++) {
+            struct half *this = &halves[i];
 
-    for (backoff = 1; ; backoff = MIN(backoff * 2, 60)) {
-        int retval = vconn_open(this->name, &this->vconn);
-        if (!retval) {
-            VLOG_WARN("%s: connected", this->name);
-            if (vconn_is_passive(this->vconn)) {
-                fatal(0, "%s: passive vconn not supported in control path",
-                      this->name);
+            rconn_run_wait(this->rconn);
+            if (!this->rxbuf) {
+                rconn_recv_wait(this->rconn);
             }
-            return;
         }
-
-        if (!reliable) {
-            fatal(0, "%s: connection failed", this->name);
-        }
-        VLOG_WARN("%s: connection failed (%s), reconnecting",
-                  this->name, strerror(errno));
-        sleep(backoff);
+        poll_block();
     }
+
+    return 0;
 }
 
 static void
 parse_options(int argc, char *argv[]) 
 {
     static struct option long_options[] = {
-        {"unreliable",  no_argument, 0, 'u'},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
+#ifdef HAVE_OPENSSL
+        {"private-key", required_argument, 0, 'p'},
+        {"certificate", required_argument, 0, 'c'},
+        {"ca-cert",     required_argument, 0, 'C'},
+#endif
         {0, 0, 0, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
@@ -213,10 +163,6 @@ parse_options(int argc, char *argv[])
         }
 
         switch (c) {
-        case 'u':
-            reliable = false;
-            break;
-
         case 'h':
             usage();
 
@@ -228,6 +174,20 @@ parse_options(int argc, char *argv[])
             vlog_set_verbosity(optarg);
             break;
 
+#ifdef HAVE_OPENSSL
+        case 'p':
+            vconn_ssl_set_private_key_file(optarg);
+            break;
+
+        case 'c':
+            vconn_ssl_set_certificate_file(optarg);
+            break;
+
+        case 'C':
+            vconn_ssl_set_ca_cert_file(optarg);
+            break;
+#endif
+
         case '?':
             exit(EXIT_FAILURE);
 
@@ -242,15 +202,25 @@ static void
 usage(void)
 {
     printf("%s: Secure Channel\n"
-           "usage: %s [OPTIONS] nl:DP_ID tcp:HOST:[PORT]\n"
-           "\nConnects to local datapath DP_ID via Netlink and \n"
-           "controller on HOST via TCP to PORT (default: %d).\n"
-           "\nNetworking options:\n"
-           "  -u, --unreliable        do not reconnect after connections drop\n"
-           "\nOther options:\n"
+           "usage: %s [OPTIONS] LOCAL REMOTE\n"
+           "\nRelays OpenFlow message between LOCAL and REMOTE datapaths.\n"
+           "LOCAL and REMOTE must each be one of the following:\n"
+           "  tcp:HOST[:PORT]         PORT (default: %d) on remote TCP HOST\n",
+           program_name, program_name, OFP_TCP_PORT);
+#ifdef HAVE_NETLINK
+    printf("  nl:DP_IDX               local datapath DP_IDX\n");
+#endif
+#ifdef HAVE_OPENSSL
+    printf("  ssl:HOST[:PORT]         SSL PORT (default: %d) on remote HOST\n"
+           "\nPKI configuration (required to use SSL):\n"
+           "  -p, --private-key=FILE  file with private key\n"
+           "  -c, --certificate=FILE  file with certificate for private key\n"
+           "  -C, --ca-cert=FILE      file with peer CA certificate\n",
+           OFP_SSL_PORT);
+#endif
+    printf("\nOther options:\n"
            "  -v, --verbose           set maximum verbosity level\n"
            "  -h, --help              display this help message\n"
-           "  -V, --version           display version information\n",
-           program_name, program_name, OFP_TCP_PORT);
+           "  -V, --version           display version information\n");
     exit(EXIT_SUCCESS);
 }