#include <poll.h>
#include <stddef.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/un.h>
: 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)
{
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);
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;
}
}
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);
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;
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;
}
}
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
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) {
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);
}
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 "
.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
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
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