From 78ff02708b11df94ac2cdf6fe82dc922758c7e30 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 12 Jun 2009 17:05:51 -0700 Subject: [PATCH] vconn: Factor out common code from TCP and SSL vconns. 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 | 167 ++++++++++++++++++++++++++++++++ lib/socket-util.h | 4 + lib/vconn-ssl.c | 98 +++---------------- lib/vconn-stream.c | 18 +--- lib/vconn-tcp.c | 79 +++------------ lib/vconn-unix.c | 15 ++- lib/vconn.c | 8 +- secchan/secchan.8.in | 10 +- utilities/ovs-controller.8.in | 10 +- vswitchd/ovs-vswitchd.conf.5.in | 5 +- 10 files changed, 233 insertions(+), 181 deletions(-) diff --git a/lib/socket-util.c b/lib/socket-util.c index 3d290e883..fdd8e95ae 100644 --- a/lib/socket-util.c +++ b/lib/socket-util.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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 "[:]", where is required and + * is optional, with 'default_port' assumed if 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 "[][:]", + * where both and are optional. If is omitted, it defaults + * to 'default_port'; if 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) { diff --git a/lib/socket-util.h b/lib/socket-util.h index bdfb3dcb5..1ee1b2cf2 100644 --- a/lib/socket-util.h +++ b/lib/socket-util.h @@ -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); diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c index 96890e6b3..01c826f96 100644 --- a/lib/vconn-ssl.c +++ b/lib/vconn-ssl.c @@ -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); diff --git a/lib/vconn-stream.c b/lib/vconn-stream.c index 468c112cc..8470da3ce 100644 --- a/lib/vconn-stream.c +++ b/lib/vconn-stream.c @@ -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; diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c index 3b29a290c..0ff163ef5 100644 --- a/lib/vconn-tcp.c +++ b/lib/vconn-tcp.c @@ -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 diff --git a/lib/vconn-unix.c b/lib/vconn-unix.c index 54b5e23db..65d0d23d5 100644 --- a/lib/vconn-unix.c +++ b/lib/vconn-unix.c @@ -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); } diff --git a/lib/vconn.c b/lib/vconn.c index b4840512f..2075300f8 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -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 " diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index bbab7fba6..3b781aeb5 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -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 diff --git a/utilities/ovs-controller.8.in b/utilities/ovs-controller.8.in index 31c7a865c..380ddeca1 100644 --- a/utilities/ovs-controller.8.in +++ b/utilities/ovs-controller.8.in @@ -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 diff --git a/vswitchd/ovs-vswitchd.conf.5.in b/vswitchd/ovs-vswitchd.conf.5.in index de0d6ea2f..8812c65ad 100644 --- a/vswitchd/ovs-vswitchd.conf.5.in +++ b/vswitchd/ovs-vswitchd.conf.5.in @@ -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 -- 2.43.0