flexnbd-proxy: Add UNIX socket support for the listen address

This commit is contained in:
nick
2013-04-15 16:52:54 +01:00
parent 4e70db8d7f
commit 33ee19dc5a
6 changed files with 78 additions and 32 deletions

View File

@@ -27,13 +27,12 @@ returning the response to the client.
USAGE
-----
$ flexnbd-proxy --addr <ADDR> --port <PORT>
$ flexnbd-proxy --addr <ADDR> [ --port <PORT> ]
--conn-addr <ADDR> --conn-port <PORT> [--bind <ADDR>] [option]*
Proxy requests from an NBD client to an NBD server, resiliently. Only one
client can be connected (to the address specified by --addr and --port) at a
time, and ACLs cannot be applied to the client, as they can be to clients
connecting directly to a flexnbd in serve mode.
client can be connected at a time, and ACLs cannot be applied to the client, as they
can be to clients connecting directly to a flexnbd in serve mode.
On starting up, the proxy will attempt to connect to the server specified by
--conn-addr and --conn-port (from the address specified by --bind, if given). If
@@ -62,10 +61,11 @@ Options
~~~~~~~
*--addr, -l ADDR*:
The address to listen on. Required.
The address to listen on. If this begins with a '/', it is assumed to be
a UNIX domain socket to create. Otherwise, it should be an IPv4 or IPv6
address.
*--port, -p PORT*:
The port to listen on. Required.
The port to listen on, if --addr is not a UNIX socket.
*--conn-addr, -C ADDR*:
The address of the NBD server to connect to. Required.
@@ -161,17 +161,16 @@ Current issues include:
* Only old-style NBD negotiation is supported
* Only one request may be in-flight at a time
* All I/O is blocking, and signals terminate the process immediately
* No UNIX socket support
* UNIX socket support is limited to the listen address
* FLUSH and TRIM commands, and the FUA flag, are not supported
* DISCONNECT requests do not get passed through to the NBD server
* No active timeout-retry of requests - we trust the kernel's idea of failure
AUTHOR
------
Written by Alex Young <alex@bytemark.co.uk>.
Original concept and core code by Matthew Bloch <matthew@bytemark.co.uk>.
Some additions by Nick Thomas <nick@bytemark.co.uk>
Proxy mode written by Nick Thomas <nick@bytemark.co.uk>
COPYING
-------

View File

@@ -8,6 +8,7 @@ int atoi(const char *nptr);
((x) >= 'A' && (x) <= 'F' ) || \
(x) == ':' || (x) == '.' \
)
/* FIXME: should change this to return negative on error like everything else */
int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
{
@@ -47,6 +48,22 @@ int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
return 0;
}
int parse_to_sockaddr(struct sockaddr* out, char* address)
{
struct sockaddr_un* un = (struct sockaddr_un*) out;
NULLCHECK( address );
if ( address[0] == '/' ) {
un->sun_family = AF_UNIX;
strncpy( un->sun_path, address, 108 ); /* FIXME: linux only */
return 1;
}
return parse_ip_to_sockaddr( out, address );
}
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
{
struct ip_and_mask* list;

View File

@@ -2,6 +2,8 @@
#define PARSE_H
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
@@ -10,6 +12,7 @@ union mysockaddr {
struct sockaddr generic;
struct sockaddr_in v4;
struct sockaddr_in6 v6;
struct sockaddr_un un;
};
struct ip_and_mask {
@@ -18,6 +21,7 @@ struct ip_and_mask {
};
int parse_ip_to_sockaddr(struct sockaddr* out, char* src);
int parse_to_sockaddr(struct sockaddr* out, char* src);
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries);
void parse_port( char *s_port, struct sockaddr_in *out );

View File

@@ -19,10 +19,11 @@ static struct option proxy_options[] = {
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
static char proxy_help_text[] =
"Usage: flexnbd-proxy <options>\n\n"
"Resiliently proxy an NBD connection between client and server\n\n"
"Resiliently proxy an NBD connection between client and server\n"
"We can listen on TCP or UNIX socket, but only connect to TCP servers.\n\n"
HELP_LINE
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
"\t--" OPT_PORT ",-p <PORT>\tThe port we will bind to as a proxy.\n"
"\t--" OPT_PORT ",-p <PORT>\tThe port we will bind to as a proxy, if required.\n"
"\t--" OPT_CONNECT_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort of the proxied server.\n"
"\t--" OPT_BIND ",-b <ADDR>\tThe address we connect from, as a proxy.\n"
@@ -114,8 +115,8 @@ int main( int argc, char *argv[] )
);
}
if ( NULL == downstream_addr || NULL == downstream_port ){
fprintf( stderr, "both --addr and --port are required.\n" );
if ( NULL == downstream_addr ){
fprintf( stderr, "--addr is required.\n" );
exit_err( proxy_help_text );
} else if ( NULL == upstream_addr || NULL == upstream_port ){
fprintf( stderr, "both --conn-addr and --conn-port are required.\n" );
@@ -136,10 +137,17 @@ int main( int argc, char *argv[] )
sigaction(SIGINT, &exit_action, NULL);
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
info(
"Proxying between %s %s (downstream) and %s %s (upstream)",
downstream_addr, downstream_port, upstream_addr, upstream_port
);
if ( NULL != downstream_port ) {
info(
"Proxying between %s %s (downstream) and %s %s (upstream)",
downstream_addr, downstream_port, upstream_addr, upstream_port
);
} else {
info(
"Proxying between %s (downstream) and %s %s (upstream)",
downstream_addr, upstream_addr, upstream_port
);
}
success = do_proxy( proxy );
proxy_destroy( proxy );

View File

@@ -24,15 +24,15 @@ struct proxier* proxy_create(
NULLCHECK( s_downstream_address );
FATAL_UNLESS(
parse_ip_to_sockaddr( &out->listen_on.generic, s_downstream_address ),
"Couldn't parse downstream address '%s' (use 0 if "
"you want to bind all IPs)",
s_downstream_address
parse_to_sockaddr( &out->listen_on.generic, s_downstream_address ),
"Couldn't parse downstream address %s"
);
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
NULLCHECK( s_downstream_port );
parse_port( s_downstream_port, &out->listen_on.v4 );
if ( out->listen_on.family != AF_UNIX ) {
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
NULLCHECK( s_downstream_port );
parse_port( s_downstream_port, &out->listen_on.v4 );
}
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
NULLCHECK( s_upstream_address );
@@ -143,10 +143,12 @@ void proxy_open_listen_socket(struct proxier* params)
SHOW_ERRNO( "Couldn't set SO_REUSEADDR" )
);
FATAL_IF_NEGATIVE(
sock_set_tcp_nodelay(params->listen_fd, 1),
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
);
if( AF_UNIX != params->listen_on.family ) {
FATAL_IF_NEGATIVE(
sock_set_tcp_nodelay(params->listen_fd, 1),
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
);
}
FATAL_UNLESS_ZERO(
sock_try_bind( params->listen_fd, &params->listen_on.generic ),
@@ -379,8 +381,10 @@ int proxy_accept( struct proxier* params )
if ( FD_ISSET( params->listen_fd, &fds ) ) {
client_fd = accept( params->listen_fd, &client_address.generic, &socklen );
if ( sock_set_tcp_nodelay(client_fd, 1) == -1 ) {
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) );
if ( client_address.family != AF_UNIX ) {
if ( sock_set_tcp_nodelay(client_fd, 1) == -1 ) {
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) );
}
}
info( "Accepted nbd client socket fd %d", client_fd );
@@ -440,6 +444,12 @@ void proxy_cleanup( struct proxier* proxy )
proxy->upstream_fd = -1;
}
if ( AF_UNIX == proxy->listen_on.family ) {
if ( -1 == unlink( proxy->listen_on.un.sun_path ) ) {
warn( SHOW_ERRNO( "Failed to unlink %s", proxy->listen_on.un.sun_path ) );
}
}
debug( "Cleanup done" );
}

View File

@@ -4,12 +4,14 @@
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include "sockutil.h"
#include "util.h"
size_t sockaddr_size( const struct sockaddr* sa )
{
struct sockaddr_un* un = (struct sockaddr_un*) sa;
size_t ret = 0;
switch( sa->sa_family ) {
@@ -19,6 +21,9 @@ size_t sockaddr_size( const struct sockaddr* sa )
case AF_INET6:
ret = sizeof( struct sockaddr_in6 );
break;
case AF_UNIX:
ret = sizeof( un->sun_family ) + SUN_LEN( un );
break;
}
return ret;
@@ -31,6 +36,7 @@ const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size
struct sockaddr_in* in = ( struct sockaddr_in* ) sa;
struct sockaddr_in6* in6 = ( struct sockaddr_in6* ) sa;
struct sockaddr_un* un = ( struct sockaddr_un* ) sa;
unsigned short real_port = ntohs( in->sin_port ); // common to in and in6
size_t size;
@@ -42,13 +48,15 @@ const char* sockaddr_address_string( const struct sockaddr* sa, char* dest, size
ret = inet_ntop( AF_INET, &in->sin_addr, dest, len );
} else if ( sa->sa_family == AF_INET6 ) {
ret = inet_ntop( AF_INET6, &in6->sin6_addr, dest, len );
} else if ( sa->sa_family == AF_UNIX ) {
ret = strncpy( dest, un->sun_path, SUN_LEN( un ) );
}
if ( ret == NULL ) {
strncpy( dest, "???", len );
}
if ( NULL != ret && real_port > 0 ) {
if ( NULL != ret && real_port > 0 && sa->sa_family != AF_UNIX ) {
size = strlen( dest );
snprintf( dest + size, len - size, " port %d", real_port );
}