vconn: Factor out common code from TCP and SSL vconns.
authorBen Pfaff <blp@nicira.com>
Sat, 13 Jun 2009 00:05:51 +0000 (17:05 -0700)
committerBen Pfaff <blp@nicira.com>
Sat, 13 Jun 2009 00:05:51 +0000 (17:05 -0700)
The TCP and SSL vconn implementations had a lot of common code to make
and accept TCP connections, which this commit factors out into common
functions in socket-util.c.

Also adds the ability to bind ptcp and pssl vconns to a particular IP
address instead of the wildcard address.

lib/socket-util.c
lib/socket-util.h
lib/vconn-ssl.c
lib/vconn-stream.c
lib/vconn-tcp.c
lib/vconn-unix.c
lib/vconn.c
secchan/secchan.8.in
utilities/ovs-controller.8.in
vswitchd/ovs-vswitchd.conf.5.in

index 3d290e8..fdd8e95 100644 (file)
@@ -23,6 +23,7 @@
 #include <poll.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/resource.h>
 #include <sys/un.h>
@@ -299,6 +300,172 @@ guess_netmask(uint32_t ip)
             : htonl(0));                          /* ??? */
 }
 
+/* Opens a non-blocking TCP socket and connects to 'target', which should be a
+ * string in the format "<host>[:<port>]", where <host> is required and <port>
+ * is optional, with 'default_port' assumed if <port> is omitted.
+ *
+ * On success, returns 0 (indicating connection complete) or EAGAIN (indicating
+ * connection in progress), in which case the new file descriptor is stored
+ * into '*fdp'.  On failure, returns a positive errno value other than EAGAIN
+ * and stores -1 into '*fdp'.
+ *
+ * If 'sinp' is non-null, then on success the target address is stored into
+ * '*sinp'. */
+int
+tcp_open_active(const char *target_, uint16_t default_port,
+                struct sockaddr_in *sinp, int *fdp)
+{
+    char *target = xstrdup(target_);
+    char *save_ptr = NULL;
+    const char *host_name;
+    const char *port_string;
+    struct sockaddr_in sin;
+    int fd = -1;
+    int error;
+
+    /* Defaults. */
+    memset(&sin, 0, sizeof sin);
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(default_port);
+
+    /* Tokenize. */
+    host_name = strtok_r(target, ":", &save_ptr);
+    port_string = strtok_r(NULL, ":", &save_ptr);
+    if (!host_name) {
+        ovs_error(0, "%s: bad peer name format", target_);
+        error = EAFNOSUPPORT;
+        goto exit;
+    }
+
+    /* Look up IP, port. */
+    error = lookup_ip(host_name, &sin.sin_addr);
+    if (error) {
+        goto exit;
+    }
+    if (port_string && atoi(port_string)) {
+        sin.sin_port = htons(atoi(port_string));
+    }
+
+    /* Create non-blocking socket. */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        VLOG_ERR("%s: socket: %s", target_, strerror(errno));
+        error = errno;
+        goto exit;
+    }
+    error = set_nonblocking(fd);
+    if (error) {
+        goto exit_close;
+    }
+
+    /* Connect. */
+    error = connect(fd, (struct sockaddr *) &sin, sizeof sin) == 0 ? 0 : errno;
+    if (error == EINPROGRESS) {
+        error = EAGAIN;
+    } else if (error && error != EAGAIN) {
+        goto exit_close;
+    }
+
+    /* Success: error is 0 or EAGAIN. */
+    goto exit;
+
+exit_close:
+    close(fd);
+exit:
+    if (!error || error == EAGAIN) {
+        if (sinp) {
+            *sinp = sin;
+        }
+        *fdp = fd;
+    } else {
+        *fdp = -1;
+    }
+    free(target);
+    return error;
+}
+
+/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming
+ * connections.  'target' should be a string in the format "[<port>][:<ip>]",
+ * where both <port> and <ip> are optional.  If <port> is omitted, it defaults
+ * to 'default_port'; if <ip> is omitted it defaults to the wildcard IP
+ * address.
+ *
+ * The socket will have SO_REUSEADDR turned on.
+ *
+ * On success, returns a non-negative file descriptor.  On failure, returns a
+ * negative errno value. */
+int
+tcp_open_passive(const char *target_, uint16_t default_port)
+{
+    char *target = xstrdup(target_);
+    char *string_ptr = target;
+    struct sockaddr_in sin;
+    const char *host_name;
+    const char *port_string;
+    int fd, error;
+    unsigned int yes  = 1;
+
+    /* Address defaults. */
+    memset(&sin, 0, sizeof sin);
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = htonl(INADDR_ANY);
+    sin.sin_port = htons(default_port);
+
+    /* Parse optional port number. */
+    port_string = strsep(&string_ptr, ":");
+    if (port_string && atoi(port_string)) {
+        sin.sin_port = htons(atoi(port_string));
+    }
+
+    /* Parse optional bind IP. */
+    host_name = strsep(&string_ptr, ":");
+    if (host_name && host_name[0]) {
+        error = lookup_ip(host_name, &sin.sin_addr);
+        if (error) {
+            goto exit;
+        }
+    }
+
+    /* Create non-blocking socket, set SO_REUSEADDR. */
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (fd < 0) {
+        error = errno;
+        VLOG_ERR("%s: socket: %s", target_, strerror(error));
+        goto exit;
+    }
+    error = set_nonblocking(fd);
+    if (error) {
+        goto exit_close;
+    }
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
+        error = errno;
+        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error));
+        goto exit_close;
+    }
+
+    /* Bind. */
+    if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
+        error = errno;
+        VLOG_ERR("%s: bind: %s", target_, strerror(error));
+        goto exit_close;
+    }
+
+    /* Listen. */
+    if (listen(fd, 10) < 0) {
+        error = errno;
+        VLOG_ERR("%s: listen: %s", target_, strerror(error));
+        goto exit_close;
+    }
+    error = 0;
+    goto exit;
+
+exit_close:
+    close(fd);
+exit:
+    free(target);
+    return error ? -error : fd;
+}
+
 int
 read_fully(int fd, void *p_, size_t size, size_t *bytes_read)
 {
index bdfb3dc..1ee1b2c 100644 (file)
@@ -33,6 +33,10 @@ int make_unix_socket(int style, bool nonblock, bool passcred,
 int get_unix_name_len(socklen_t sun_len);
 uint32_t guess_netmask(uint32_t ip);
 
+int tcp_open_active(const char *target, uint16_t default_port,
+                    struct sockaddr_in *sinp, int *fdp);
+int tcp_open_passive(const char *target, uint16_t default_port);
+
 int read_fully(int fd, void *, size_t, size_t *bytes_read);
 int write_fully(int fd, const void *, size_t, size_t *bytes_written);
 
index 96890e6..01c826f 100644 (file)
@@ -269,58 +269,21 @@ ssl_vconn_cast(struct vconn *vconn)
 static int
 ssl_open(const char *name, char *suffix, struct vconn **vconnp)
 {
-    char *save_ptr, *host_name, *port_string;
     struct sockaddr_in sin;
-    int retval;
-    int fd;
-
-    retval = ssl_init();
-    if (retval) {
-        return retval;
-    }
-
-    host_name = strtok_r(suffix, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
-    if (!host_name) {
-        ovs_error(0, "%s: bad peer name format", name);
-        return EAFNOSUPPORT;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    if (lookup_ip(host_name, &sin.sin_addr)) {
-        return ENOENT;
-    }
-    sin.sin_port = htons(port_string && *port_string ? atoi(port_string)
-                         : OFP_SSL_PORT);
+    int error, fd;
 
-    /* Create socket. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
+    error = ssl_init();
+    if (error) {
+        return error;
     }
 
-    /* Connect socket. */
-    retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        if (errno == EINPROGRESS) {
-            return new_ssl_vconn(name, fd, CLIENT, STATE_TCP_CONNECTING,
-                                 &sin, vconnp);
-        } else {
-            int error = errno;
-            VLOG_ERR("%s: connect: %s", name, strerror(error));
-            close(fd);
-            return error;
-        }
+    error = tcp_open_active(suffix, OFP_SSL_PORT, &sin, &fd);
+    if (fd >= 0) {
+        int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
+        return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp);
     } else {
-        return new_ssl_vconn(name, fd, CLIENT, STATE_SSL_CONNECTING,
-                             &sin, vconnp);
+        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        return error;
     }
 }
 
@@ -793,55 +756,18 @@ pssl_pvconn_cast(struct pvconn *pvconn)
 static int
 pssl_open(const char *name, char *suffix, struct pvconn **pvconnp)
 {
-    struct sockaddr_in sin;
     struct pssl_pvconn *pssl;
     int retval;
     int fd;
-    unsigned int yes = 1;
 
     retval = ssl_init();
     if (retval) {
         return retval;
     }
 
-    /* Create socket. */
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = tcp_open_passive(suffix, OFP_SSL_PORT);
     if (fd < 0) {
-        int error = errno;
-        VLOG_ERR("%s: socket: %s", name, strerror(error));
-        return error;
-    }
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
-        int error = errno;
-        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
-        return error;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    sin.sin_addr.s_addr = htonl(INADDR_ANY);
-    sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_SSL_PORT);
-    retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: bind: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    retval = listen(fd, 10);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: listen: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
+        return -fd;
     }
 
     pssl = xmalloc(sizeof *pssl);
index 468c112..8470da3 100644 (file)
@@ -270,23 +270,7 @@ new_pstream_pvconn(const char *name, int fd,
                                    size_t sa_len, struct vconn **),
                   struct pvconn **pvconnp)
 {
-    struct pstream_pvconn *ps;
-    int retval;
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
-    }
-
-    if (listen(fd, 10) < 0) {
-        int error = errno;
-        VLOG_ERR("%s: listen: %s", name, strerror(error));
-        close(fd);
-        return error;
-    }
-
-    ps = xmalloc(sizeof *ps);
+    struct pstream_pvconn *ps = xmalloc(sizeof *ps);
     pvconn_init(&ps->pvconn, &pstream_pvconn_class, name);
     ps->fd = fd;
     ps->accept_cb = accept_cb;
index 3b29a29..0ff163e 100644 (file)
@@ -57,51 +57,15 @@ new_tcp_vconn(const char *name, int fd, int connect_status,
 static int
 tcp_open(const char *name, char *suffix, struct vconn **vconnp)
 {
-    char *save_ptr;
-    const char *host_name;
-    const char *port_string;
     struct sockaddr_in sin;
-    int retval;
-    int fd;
-
-    host_name = strtok_r(suffix, ":", &save_ptr);
-    port_string = strtok_r(NULL, ":", &save_ptr);
-    if (!host_name) {
-        ovs_error(0, "%s: bad peer name format", name);
-        return EAFNOSUPPORT;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    if (lookup_ip(host_name, &sin.sin_addr)) {
-        return ENOENT;
-    }
-    sin.sin_port = htons(port_string ? atoi(port_string) : OFP_TCP_PORT);
-
-    fd = socket(AF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-
-    retval = set_nonblocking(fd);
-    if (retval) {
-        close(fd);
-        return retval;
-    }
+    int fd, error;
 
-    retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        if (errno == EINPROGRESS) {
-            return new_tcp_vconn(name, fd, EAGAIN, &sin, vconnp);
-        } else {
-            int error = errno;
-            VLOG_ERR("%s: connect: %s", name, strerror(error));
-            close(fd);
-            return error;
-        }
+    error = tcp_open_active(suffix, OFP_TCP_PORT, NULL, &fd);
+    if (fd >= 0) {
+        return new_tcp_vconn(name, fd, error, &sin, vconnp);
     } else {
-        return new_tcp_vconn(name, fd, 0, &sin, vconnp);
+        VLOG_ERR("%s: connect: %s", name, strerror(error));
+        return error;
     }
 }
 
@@ -121,37 +85,16 @@ static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
                        struct vconn **vconnp);
 
 static int
-ptcp_open(const char *name, char *suffix, struct pvconn **pvconnp)
+ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
 {
-    struct sockaddr_in sin;
-    int retval;
     int fd;
-    unsigned int yes  = 1;
 
-    fd = socket(AF_INET, SOCK_STREAM, 0);
+    fd = tcp_open_passive(suffix, OFP_TCP_PORT);
     if (fd < 0) {
-        VLOG_ERR("%s: socket: %s", name, strerror(errno));
-        return errno;
-    }
-
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
-        VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
-        return errno;
-    }
-
-    memset(&sin, 0, sizeof sin);
-    sin.sin_family = AF_INET;
-    sin.sin_addr.s_addr = htonl(INADDR_ANY);
-    sin.sin_port = htons(atoi(suffix) ? atoi(suffix) : OFP_TCP_PORT);
-    retval = bind(fd, (struct sockaddr *) &sin, sizeof sin);
-    if (retval < 0) {
-        int error = errno;
-        VLOG_ERR("%s: bind: %s", name, strerror(error));
-        close(fd);
-        return error;
+        return -fd;
+    } else {
+        return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
     }
-
-    return new_pstream_pvconn("ptcp", fd, ptcp_accept, pvconnp);
 }
 
 static int
index 54b5e23..65d0d23 100644 (file)
@@ -81,7 +81,7 @@ static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
 static int
 punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
 {
-    int fd;
+    int fd, error;
 
     fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
     if (fd < 0) {
@@ -89,6 +89,19 @@ punix_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
         return errno;
     }
 
+    error = set_nonblocking(fd);
+    if (error) {
+        close(fd);
+        return error;
+    }
+
+    if (listen(fd, 10) < 0) {
+        error = errno;
+        VLOG_ERR("%s: listen: %s", name, strerror(error));
+        close(fd);
+        return error;
+    }
+
     return new_pstream_pvconn("punix", fd, punix_accept, pvconnp);
 }
 
index b484051..2075300 100644 (file)
@@ -139,12 +139,12 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
 
     if (passive) {
         printf("Passive OpenFlow connection methods:\n");
-        printf("  ptcp:[PORT]             "
-               "listen to TCP PORT (default: %d)\n",
+        printf("  ptcp:[PORT][:IP]        "
+               "listen to TCP PORT (default: %d) on IP\n",
                OFP_TCP_PORT);
 #ifdef HAVE_OPENSSL
-        printf("  pssl:[PORT]             "
-               "listen for SSL on PORT (default: %d)\n",
+        printf("  pssl:[PORT][:IP]        "
+               "listen for SSL on PORT (default: %d) on IP\n",
                OFP_SSL_PORT);
 #endif
         printf("  punix:FILE              "
index bbab7fb..3b781ae 100644 (file)
@@ -307,14 +307,20 @@ multiple connection methods.
 
 .RS
 .TP
-\fBpssl:\fR[\fIport\fR]
+\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for SSL connections on \fIport\fR (default: 6633).  The
 \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR options
 are mandatory when this form is used.
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
-\fBptcp:\fR[\fIport\fR]
+\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
 \fBpunix:\fIfile\fR
index 31c7a86..380ddec 100644 (file)
@@ -16,16 +16,22 @@ protocol, causing them to function as L2 MAC-learning switches or hub.
 one or more of the following OpenFlow connection methods:
 
 .TP
-\fBpssl:\fR[\fIport\fR]
+\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for SSL connections from remote OpenFlow switches on
 \fIport\fR (default: 6633).  The \fB--private-key\fR,
 \fB--certificate\fR, and \fB--ca-cert\fR options are mandatory when
 this form is used.
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
-\fBptcp:\fR[\fIport\fR]
+\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]
 Listens for TCP connections from remote OpenFlow switches on
 \fIport\fR (default: 6633).
+By default, \fB\*(PN\fR listens for connections to any local IP
+address, but \fIip\fR may be specified to listen only for connections
+to the given \fIip\fR.
 
 .TP
 \fBpunix:\fIfile\fR
index de0d6ea..8812c65 100644 (file)
@@ -618,8 +618,11 @@ Listens for connections on the Unix domain server socket named \fIfile\fR.
 Listens for SSL connections on \fIport\fR (default: 6633).  SSL must
 be configured when this form is used (see \fBSSL Configuration\fR,
 above).
-.IP "\fBptcp:\fR[\fIport\fR]"
+.IP "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]"
 Listens for TCP connections on \fIport\fR (default: 6633).
+By default, \fB\ovs\-vswitchd\fR listens for connections to any local
+IP address, but \fIip\fR may be specified to limit connections to the
+specified local \fIip\fR.
 .RE
 To entirely disable listening for management connections, set
 \fBbridge.\fIname\fB.openflow.listeners\fR to the single value