From 33ee19dc5ac581477d11ca5ff637f0db047b0f1d Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 15 Apr 2013 16:52:54 +0100 Subject: [PATCH] flexnbd-proxy: Add UNIX socket support for the listen address --- README.proxy.txt | 19 +++++++++---------- src/parse.c | 17 +++++++++++++++++ src/parse.h | 4 ++++ src/proxy-main.c | 24 ++++++++++++++++-------- src/proxy.c | 36 +++++++++++++++++++++++------------- src/sockutil.c | 10 +++++++++- 6 files changed, 78 insertions(+), 32 deletions(-) diff --git a/README.proxy.txt b/README.proxy.txt index 8b20abf..157a542 100644 --- a/README.proxy.txt +++ b/README.proxy.txt @@ -27,13 +27,12 @@ returning the response to the client. USAGE ----- - $ flexnbd-proxy --addr --port + $ flexnbd-proxy --addr [ --port ] --conn-addr --conn-port [--bind ] [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 . Original concept and core code by Matthew Bloch . -Some additions by Nick Thomas +Proxy mode written by Nick Thomas COPYING ------- diff --git a/src/parse.c b/src/parse.c index dea65f6..5f8c006 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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; diff --git a/src/parse.h b/src/parse.h index ec47354..afe08ea 100644 --- a/src/parse.h +++ b/src/parse.h @@ -2,6 +2,8 @@ #define PARSE_H #include +#include + #include #include @@ -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 ); diff --git a/src/proxy-main.c b/src/proxy-main.c index 595d13d..b48e8ca 100644 --- a/src/proxy-main.c +++ b/src/proxy-main.c @@ -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 \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 \tThe address we will bind to as a proxy.\n" - "\t--" OPT_PORT ",-p \tThe port we will bind to as a proxy.\n" + "\t--" OPT_PORT ",-p \tThe port we will bind to as a proxy, if required.\n" "\t--" OPT_CONNECT_ADDR ",-C \tAddress of the proxied server.\n" "\t--" OPT_CONNECT_PORT ",-P \tPort of the proxied server.\n" "\t--" OPT_BIND ",-b \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 ); diff --git a/src/proxy.c b/src/proxy.c index 1afed87..4842654 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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, ¶ms->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" ); } diff --git a/src/sockutil.c b/src/sockutil.c index 54a9d08..cb15f4f 100644 --- a/src/sockutil.c +++ b/src/sockutil.c @@ -4,12 +4,14 @@ #include #include #include +#include #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 ); }