flexnbd-proxy: Add UNIX socket support for the listen address
This commit is contained in:
@@ -27,13 +27,12 @@ returning the response to the client.
|
|||||||
USAGE
|
USAGE
|
||||||
-----
|
-----
|
||||||
|
|
||||||
$ flexnbd-proxy --addr <ADDR> --port <PORT>
|
$ flexnbd-proxy --addr <ADDR> [ --port <PORT> ]
|
||||||
--conn-addr <ADDR> --conn-port <PORT> [--bind <ADDR>] [option]*
|
--conn-addr <ADDR> --conn-port <PORT> [--bind <ADDR>] [option]*
|
||||||
|
|
||||||
Proxy requests from an NBD client to an NBD server, resiliently. Only one
|
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
|
client can be connected at a time, and ACLs cannot be applied to the client, as they
|
||||||
time, and ACLs cannot be applied to the client, as they can be to clients
|
can be to clients connecting directly to a flexnbd in serve mode.
|
||||||
connecting directly to a flexnbd in serve mode.
|
|
||||||
|
|
||||||
On starting up, the proxy will attempt to connect to the server specified by
|
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
|
--conn-addr and --conn-port (from the address specified by --bind, if given). If
|
||||||
@@ -62,10 +61,11 @@ Options
|
|||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
*--addr, -l ADDR*:
|
*--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*:
|
*--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*:
|
*--conn-addr, -C ADDR*:
|
||||||
The address of the NBD server to connect to. Required.
|
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 old-style NBD negotiation is supported
|
||||||
* Only one request may be in-flight at a time
|
* Only one request may be in-flight at a time
|
||||||
* All I/O is blocking, and signals terminate the process immediately
|
* 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
|
* FLUSH and TRIM commands, and the FUA flag, are not supported
|
||||||
* DISCONNECT requests do not get passed through to the NBD server
|
* 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
|
* No active timeout-retry of requests - we trust the kernel's idea of failure
|
||||||
|
|
||||||
AUTHOR
|
AUTHOR
|
||||||
------
|
------
|
||||||
|
|
||||||
Written by Alex Young <alex@bytemark.co.uk>.
|
Written by Alex Young <alex@bytemark.co.uk>.
|
||||||
Original concept and core code by Matthew Bloch <matthew@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
|
COPYING
|
||||||
-------
|
-------
|
||||||
|
17
src/parse.c
17
src/parse.c
@@ -8,6 +8,7 @@ int atoi(const char *nptr);
|
|||||||
((x) >= 'A' && (x) <= 'F' ) || \
|
((x) >= 'A' && (x) <= 'F' ) || \
|
||||||
(x) == ':' || (x) == '.' \
|
(x) == ':' || (x) == '.' \
|
||||||
)
|
)
|
||||||
|
|
||||||
/* FIXME: should change this to return negative on error like everything else */
|
/* FIXME: should change this to return negative on error like everything else */
|
||||||
int parse_ip_to_sockaddr(struct sockaddr* out, char* src)
|
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;
|
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)
|
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries)
|
||||||
{
|
{
|
||||||
struct ip_and_mask* list;
|
struct ip_and_mask* list;
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
#define PARSE_H
|
#define PARSE_H
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@@ -10,6 +12,7 @@ union mysockaddr {
|
|||||||
struct sockaddr generic;
|
struct sockaddr generic;
|
||||||
struct sockaddr_in v4;
|
struct sockaddr_in v4;
|
||||||
struct sockaddr_in6 v6;
|
struct sockaddr_in6 v6;
|
||||||
|
struct sockaddr_un un;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ip_and_mask {
|
struct ip_and_mask {
|
||||||
@@ -18,6 +21,7 @@ struct ip_and_mask {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int parse_ip_to_sockaddr(struct sockaddr* out, char* src);
|
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);
|
int parse_acl(struct ip_and_mask (**out)[], int max, char **entries);
|
||||||
void parse_port( char *s_port, struct sockaddr_in *out );
|
void parse_port( char *s_port, struct sockaddr_in *out );
|
||||||
|
|
||||||
|
@@ -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_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
|
||||||
static char proxy_help_text[] =
|
static char proxy_help_text[] =
|
||||||
"Usage: flexnbd-proxy <options>\n\n"
|
"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
|
HELP_LINE
|
||||||
"\t--" OPT_ADDR ",-l <ADDR>\tThe address we will bind to as a proxy.\n"
|
"\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_ADDR ",-C <ADDR>\tAddress of the proxied server.\n"
|
||||||
"\t--" OPT_CONNECT_PORT ",-P <PORT>\tPort 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"
|
"\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 ){
|
if ( NULL == downstream_addr ){
|
||||||
fprintf( stderr, "both --addr and --port are required.\n" );
|
fprintf( stderr, "--addr is required.\n" );
|
||||||
exit_err( proxy_help_text );
|
exit_err( proxy_help_text );
|
||||||
} else if ( NULL == upstream_addr || NULL == upstream_port ){
|
} else if ( NULL == upstream_addr || NULL == upstream_port ){
|
||||||
fprintf( stderr, "both --conn-addr and --conn-port are required.\n" );
|
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);
|
sigaction(SIGINT, &exit_action, NULL);
|
||||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||||
|
|
||||||
info(
|
if ( NULL != downstream_port ) {
|
||||||
"Proxying between %s %s (downstream) and %s %s (upstream)",
|
info(
|
||||||
downstream_addr, downstream_port, upstream_addr, upstream_port
|
"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 );
|
success = do_proxy( proxy );
|
||||||
proxy_destroy( proxy );
|
proxy_destroy( proxy );
|
||||||
|
36
src/proxy.c
36
src/proxy.c
@@ -24,15 +24,15 @@ struct proxier* proxy_create(
|
|||||||
NULLCHECK( s_downstream_address );
|
NULLCHECK( s_downstream_address );
|
||||||
|
|
||||||
FATAL_UNLESS(
|
FATAL_UNLESS(
|
||||||
parse_ip_to_sockaddr( &out->listen_on.generic, s_downstream_address ),
|
parse_to_sockaddr( &out->listen_on.generic, s_downstream_address ),
|
||||||
"Couldn't parse downstream address '%s' (use 0 if "
|
"Couldn't parse downstream address %s"
|
||||||
"you want to bind all IPs)",
|
|
||||||
s_downstream_address
|
|
||||||
);
|
);
|
||||||
|
|
||||||
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
|
if ( out->listen_on.family != AF_UNIX ) {
|
||||||
NULLCHECK( s_downstream_port );
|
FATAL_IF_NULL( s_downstream_port, "Downstream port not specified" );
|
||||||
parse_port( s_downstream_port, &out->listen_on.v4 );
|
NULLCHECK( s_downstream_port );
|
||||||
|
parse_port( s_downstream_port, &out->listen_on.v4 );
|
||||||
|
}
|
||||||
|
|
||||||
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
|
FATAL_IF_NULL(s_upstream_address, "Upstream address not specified");
|
||||||
NULLCHECK( s_upstream_address );
|
NULLCHECK( s_upstream_address );
|
||||||
@@ -143,10 +143,12 @@ void proxy_open_listen_socket(struct proxier* params)
|
|||||||
SHOW_ERRNO( "Couldn't set SO_REUSEADDR" )
|
SHOW_ERRNO( "Couldn't set SO_REUSEADDR" )
|
||||||
);
|
);
|
||||||
|
|
||||||
FATAL_IF_NEGATIVE(
|
if( AF_UNIX != params->listen_on.family ) {
|
||||||
sock_set_tcp_nodelay(params->listen_fd, 1),
|
FATAL_IF_NEGATIVE(
|
||||||
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
|
sock_set_tcp_nodelay(params->listen_fd, 1),
|
||||||
);
|
SHOW_ERRNO( "Couldn't set TCP_NODELAY" )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
FATAL_UNLESS_ZERO(
|
FATAL_UNLESS_ZERO(
|
||||||
sock_try_bind( params->listen_fd, ¶ms->listen_on.generic ),
|
sock_try_bind( params->listen_fd, ¶ms->listen_on.generic ),
|
||||||
@@ -379,8 +381,10 @@ int proxy_accept( struct proxier* params )
|
|||||||
if ( FD_ISSET( params->listen_fd, &fds ) ) {
|
if ( FD_ISSET( params->listen_fd, &fds ) ) {
|
||||||
client_fd = accept( params->listen_fd, &client_address.generic, &socklen );
|
client_fd = accept( params->listen_fd, &client_address.generic, &socklen );
|
||||||
|
|
||||||
if ( sock_set_tcp_nodelay(client_fd, 1) == -1 ) {
|
if ( client_address.family != AF_UNIX ) {
|
||||||
warn( SHOW_ERRNO( "Failed to set TCP_NODELAY" ) );
|
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 );
|
info( "Accepted nbd client socket fd %d", client_fd );
|
||||||
@@ -440,6 +444,12 @@ void proxy_cleanup( struct proxier* proxy )
|
|||||||
proxy->upstream_fd = -1;
|
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" );
|
debug( "Cleanup done" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,12 +4,14 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include "sockutil.h"
|
#include "sockutil.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
size_t sockaddr_size( const struct sockaddr* sa )
|
size_t sockaddr_size( const struct sockaddr* sa )
|
||||||
{
|
{
|
||||||
|
struct sockaddr_un* un = (struct sockaddr_un*) sa;
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
|
|
||||||
switch( sa->sa_family ) {
|
switch( sa->sa_family ) {
|
||||||
@@ -19,6 +21,9 @@ size_t sockaddr_size( const struct sockaddr* sa )
|
|||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
ret = sizeof( struct sockaddr_in6 );
|
ret = sizeof( struct sockaddr_in6 );
|
||||||
break;
|
break;
|
||||||
|
case AF_UNIX:
|
||||||
|
ret = sizeof( un->sun_family ) + SUN_LEN( un );
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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_in* in = ( struct sockaddr_in* ) sa;
|
||||||
struct sockaddr_in6* in6 = ( struct sockaddr_in6* ) 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
|
unsigned short real_port = ntohs( in->sin_port ); // common to in and in6
|
||||||
size_t size;
|
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 );
|
ret = inet_ntop( AF_INET, &in->sin_addr, dest, len );
|
||||||
} else if ( sa->sa_family == AF_INET6 ) {
|
} else if ( sa->sa_family == AF_INET6 ) {
|
||||||
ret = inet_ntop( AF_INET6, &in6->sin6_addr, dest, len );
|
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 ) {
|
if ( ret == NULL ) {
|
||||||
strncpy( dest, "???", len );
|
strncpy( dest, "???", len );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( NULL != ret && real_port > 0 ) {
|
if ( NULL != ret && real_port > 0 && sa->sa_family != AF_UNIX ) {
|
||||||
size = strlen( dest );
|
size = strlen( dest );
|
||||||
snprintf( dest + size, len - size, " port %d", real_port );
|
snprintf( dest + size, len - size, " port %d", real_port );
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user