mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
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.
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user