diff --git a/src/control.c b/src/control.c index e8ee026..870ad08 100644 --- a/src/control.c +++ b/src/control.c @@ -173,14 +173,17 @@ int control_mirror(struct control_params* client, int linesc, char** lines) int fd, map_fd; struct mirror_status *mirror; union mysockaddr connect_to; + union mysockaddr connect_from; + int use_connect_from = 0; uint64_t max_bytes_per_second; int action_at_finish; + if (linesc < 2) { write_socket("1: mirror takes at least two parameters"); return -1; } - + if (parse_ip_to_sockaddr(&connect_to.generic, lines[0]) == 0) { write_socket("1: bad IP address"); return -1; @@ -192,14 +195,22 @@ int control_mirror(struct control_params* client, int linesc, char** lines) return -1; } connect_to.v4.sin_port = htobe16(connect_to.v4.sin_port); - - max_bytes_per_second = 0; + if (linesc > 2) { + if (parse_ip_to_sockaddr(&connect_from.generic, lines[2]) == 0) { + write_socket("1: bad bind address"); + return -1; + } + use_connect_from = 1; + } + + max_bytes_per_second = 0; + if (linesc > 3) { max_bytes_per_second = atoi(lines[2]); } action_at_finish = ACTION_PROXY; - if (linesc > 3) { + if (linesc > 4) { if (strcmp("proxy", lines[3]) == 0) action_at_finish = ACTION_PROXY; else if (strcmp("exit", lines[3]) == 0) @@ -212,12 +223,17 @@ int control_mirror(struct control_params* client, int linesc, char** lines) } } - if (linesc > 4) { + if (linesc > 5) { write_socket("1: unrecognised parameters to mirror"); return -1; } + + /** I don't like use_connect_from but socket_connect doesn't take *mysockaddr :( */ + if (use_connect_from) + fd = socket_connect(&connect_to.generic, &connect_from.generic); + else + fd = socket_connect(&connect_to.generic, NULL); - fd = socket_connect(&connect_to.generic); remote_size = socket_nbd_read_hello(fd); remote_size = remote_size; // shush compiler diff --git a/src/flexnbd.c b/src/flexnbd.c index 3ef2d23..f0ae799 100644 --- a/src/flexnbd.c +++ b/src/flexnbd.c @@ -111,6 +111,7 @@ void params_readwrite( struct mode_readwrite_params* out, char* s_ip_address, char* s_port, + char* s_bind_address, char* s_from, char* s_length_or_filename ) @@ -128,6 +129,9 @@ void params_readwrite( SERVER_ERROR("Couldn't parse connection address '%s'", s_ip_address); + if (s_bind_address != NULL && parse_ip_to_sockaddr(&out->connect_from.generic, s_bind_address) == 0) + SERVER_ERROR("Couldn't parse bind address '%s'", s_bind_address); + /* FIXME: duplicated from above */ out->connect_to.v4.sin_port = atoi(s_port); if (out->connect_to.v4.sin_port < 0 || out->connect_to.v4.sin_port > 65535) @@ -198,7 +202,7 @@ void read_serve_param( int c, char **ip_addr, char **ip_port, char **file, char } -void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **from, char **size) +void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **bind_addr, char **from, char **size) { switch(c){ case 'h': @@ -217,6 +221,9 @@ void read_readwrite_param( int c, char **ip_addr, char **ip_port, char **from, c case 'S': *size = optarg; break; + case 'b': + *bind_addr = optarg; + break; case 'v': set_debug(1); break; @@ -250,7 +257,7 @@ void read_acl_param( int c, char **sock ) read_sock_param( c, sock, acl_help_text ); } -void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port ) +void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port, char **bind_addr ) { switch( c ){ case 'h': @@ -266,6 +273,8 @@ void read_mirror_param( int c, char **sock, char **ip_addr, char **ip_port ) case 'p': *ip_port = optarg; break; + case 'b': + *bind_addr = optarg; case 'v': set_debug(1); break; @@ -320,8 +329,9 @@ int mode_serve( int argc, char *argv[] ) int mode_read( int argc, char *argv[] ) { int c; - char *ip_addr = NULL; - char *ip_port = NULL; + char *ip_addr = NULL; + char *ip_port = NULL; + char *bind_addr = NULL; char *from = NULL; char *size = NULL; int err = 0; @@ -330,8 +340,11 @@ int mode_read( int argc, char *argv[] ) while (1){ c = getopt_long(argc, argv, read_short_options, read_options, NULL); - if ( c == -1 ) break; - read_readwrite_param( c, &ip_addr, &ip_port, &from, &size ); + + if ( c == -1 ) + break; + + read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size ); } if ( NULL == ip_addr || NULL == ip_port ) { @@ -345,7 +358,7 @@ int mode_read( int argc, char *argv[] ) if ( err ) { exit_err( read_help_text ); } memset( &readwrite, 0, sizeof( readwrite ) ); - params_readwrite( 0, &readwrite, ip_addr, ip_port, from, size ); + params_readwrite( 0, &readwrite, ip_addr, ip_port, bind_addr, from, size ); do_read( &readwrite ); return 0; } @@ -353,8 +366,9 @@ int mode_read( int argc, char *argv[] ) int mode_write( int argc, char *argv[] ) { int c; - char *ip_addr = NULL; - char *ip_port = NULL; + char *ip_addr = NULL; + char *ip_port = NULL; + char *bind_addr = NULL; char *from = NULL; char *size = NULL; int err = 0; @@ -363,8 +377,10 @@ int mode_write( int argc, char *argv[] ) while (1){ c = getopt_long(argc, argv, write_short_options, write_options, NULL); - if ( c == -1 ) break; - read_readwrite_param( c, &ip_addr, &ip_port, &from, &size ); + if ( c == -1 ) + break; + + read_readwrite_param( c, &ip_addr, &ip_port, &bind_addr, &from, &size ); } if ( NULL == ip_addr || NULL == ip_port ) { @@ -378,7 +394,7 @@ int mode_write( int argc, char *argv[] ) if ( err ) { exit_err( write_help_text ); } memset( &readwrite, 0, sizeof( readwrite ) ); - params_readwrite( 1, &readwrite, ip_addr, ip_port, from, size ); + params_readwrite( 1, &readwrite, ip_addr, ip_port, bind_addr, from, size ); do_write( &readwrite ); return 0; } @@ -412,13 +428,13 @@ int mode_mirror( int argc, char *argv[] ) { int c; char *sock = NULL; - char *remote_argv[3] = {0}; + char *remote_argv[4] = {0}; int err = 0; while (1) { c = getopt_long( argc, argv, mirror_short_options, mirror_options, NULL); if ( -1 == c ) break; - read_mirror_param( c, &sock, &remote_argv[0], &remote_argv[1] ); + read_mirror_param( c, &sock, &remote_argv[0], &remote_argv[1], &remote_argv[2] ); } if ( NULL == sock ){ @@ -430,8 +446,11 @@ int mode_mirror( int argc, char *argv[] ) err = 1; } if ( err ) { exit_err( mirror_help_text ); } - - do_remote_command( "mirror", sock, 2, remote_argv ); + + if (argv[2] == NULL) + do_remote_command( "mirror", sock, 2, remote_argv ); + else + do_remote_command( "mirror", sock, 3, remote_argv ); return 0; } diff --git a/src/options.h b/src/options.h index 5637fea..d1425a0 100644 --- a/src/options.h +++ b/src/options.h @@ -5,6 +5,7 @@ #define OPT_HELP "help" #define OPT_ADDR "addr" +#define OPT_BIND "bind" #define OPT_PORT "port" #define OPT_FILE "file" #define OPT_SOCK "sock" @@ -36,9 +37,7 @@ #define GETOPT_SOCK GETOPT_ARG( OPT_SOCK, 's' ) #define GETOPT_FROM GETOPT_ARG( OPT_FROM, 'F' ) #define GETOPT_SIZE GETOPT_ARG( OPT_SIZE, 'S' ) - -#define HELP_LINE "\t--" OPT_HELP ",-h\t\tThis text.\n" - +#define GETOPT_BIND GETOPT_ARG( OPT_BIND, 'b' ) #ifdef DEBUG # define OPT_VERBOSE "verbose" @@ -52,6 +51,12 @@ # define SOPT_VERBOSE "" #endif +#define HELP_LINE \ + "\t--" OPT_HELP ",-h \tThis text.\n" +#define SOCK_LINE \ + "\t--" OPT_SOCK ",-s \tPath to the control socket.\n" +#define BIND_LINE \ + "\t--" OPT_BIND ",-b \tBind the local socket to a particular IP address.\n" static struct option serve_options[] = { @@ -72,8 +77,8 @@ static char serve_help_text[] = "\t--" OPT_ADDR ",-l \tThe address to serve on.\n" "\t--" OPT_PORT ",-p \tThe port to serve on.\n" "\t--" OPT_FILE ",-f \tThe file to serve.\n" - "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL\n" - "\t--" OPT_SOCK ",-s \tPath to the control socket to open.\n" + "\t--" OPT_DENY ",-d\tDeny connections by default unless in ACL.\n" + SOCK_LINE VERBOSE_LINE; static struct option read_options[] = { @@ -82,10 +87,11 @@ static struct option read_options[] = { GETOPT_PORT, GETOPT_FROM, GETOPT_SIZE, + GETOPT_BIND, GETOPT_VERBOSE, {0} }; -static char read_short_options[] = "hl:p:F:S:" SOPT_VERBOSE; +static char read_short_options[] = "hl:p:F:S:b:" SOPT_VERBOSE; static char read_help_text[] = "Usage: flexnbd " CMD_READ " \n\n" "Read SIZE bytes from a server at ADDR:PORT to stdout, starting at OFFSET.\n\n" @@ -94,6 +100,7 @@ static char read_help_text[] = "\t--" OPT_PORT ",-p \tThe port to read from.\n" "\t--" OPT_FROM ",-F \tByte offset to read from.\n" "\t--" OPT_SIZE ",-S \tBytes to read.\n" + BIND_LINE VERBOSE_LINE; @@ -107,6 +114,7 @@ static char write_help_text[] = "\t--" OPT_PORT ",-p \tThe port to write to.\n" "\t--" OPT_FROM ",-F \tByte offset to write from.\n" "\t--" OPT_SIZE ",-S \tBytes to write.\n" + BIND_LINE VERBOSE_LINE; struct option acl_options[] = { @@ -120,7 +128,7 @@ static char acl_help_text[] = "Usage: flexnbd " CMD_ACL " [+]\n\n" "Set the access control list for a server with control socket SOCK.\n\n" HELP_LINE - "\t--" OPT_SOCK ",-s \tPath to the control socket.\n" + SOCK_LINE VERBOSE_LINE; struct option mirror_options[] = { @@ -128,17 +136,19 @@ struct option mirror_options[] = { GETOPT_SOCK, GETOPT_ADDR, GETOPT_PORT, + GETOPT_BIND, GETOPT_VERBOSE, {0} }; -static char mirror_short_options[] = "hs:l:p:" SOPT_VERBOSE; +static char mirror_short_options[] = "hs:l:p:b:" SOPT_VERBOSE; static char mirror_help_text[] = "Usage: flexnbd " CMD_MIRROR " \n\n" "Start mirroring from the server with control socket SOCK to one at ADDR:PORT.\n\n" HELP_LINE - "\t--" OPT_SOCK ",-s \tPath to the control socket.\n" "\t--" OPT_ADDR ",-l \tThe address to mirror to.\n" "\t--" OPT_PORT ",-p \tThe port to mirror to.\n" + SOCK_LINE + BIND_LINE VERBOSE_LINE; @@ -153,7 +163,7 @@ static char status_help_text[] = "Usage: flexnbd " CMD_STATUS " \n\n" "Get the status for a server with control socket SOCK.\n\n" HELP_LINE - "\t--" OPT_SOCK ",-s \tPath to the control socket.\n" + SOCK_LINE VERBOSE_LINE; static char help_help_text[] = @@ -167,3 +177,4 @@ static char help_help_text[] = "\tflexnbd status\n" "\tflexnbd help\n\n" "See flexnbd help for further info\n"; + diff --git a/src/readwrite.c b/src/readwrite.c index b4c142e..aca8648 100644 --- a/src/readwrite.c +++ b/src/readwrite.c @@ -7,12 +7,20 @@ #include #include -int socket_connect(struct sockaddr* to) +int socket_connect(struct sockaddr* to, struct sockaddr* from) { int fd = socket(to->sa_family == AF_INET ? PF_INET : PF_INET6, SOCK_STREAM, 0); SERVER_ERROR_ON_FAILURE(fd, "Couldn't create client socket"); - SERVER_ERROR_ON_FAILURE(connect(fd, to, sizeof(struct sockaddr_in6)), - "connect failed"); + + if (NULL != from) + SERVER_ERROR_ON_FAILURE( + bind(fd, from, sizeof(struct sockaddr_in6)), + "bind() failed" + ); + + SERVER_ERROR_ON_FAILURE( + connect(fd, to, sizeof(struct sockaddr_in6)),"connect failed" + ); return fd; } @@ -106,7 +114,7 @@ void socket_nbd_write(int fd, off64_t from, int len, int in_fd, void* in_buf) void do_read(struct mode_readwrite_params* params) { - params->client = socket_connect(¶ms->connect_to.generic); + params->client = socket_connect(¶ms->connect_to.generic, ¶ms->connect_from.generic); CHECK_RANGE("read"); socket_nbd_read(params->client, params->from, params->len, params->data_fd, NULL); @@ -115,7 +123,7 @@ void do_read(struct mode_readwrite_params* params) void do_write(struct mode_readwrite_params* params) { - params->client = socket_connect(¶ms->connect_to.generic); + params->client = socket_connect(¶ms->connect_to.generic, ¶ms->connect_from.generic); CHECK_RANGE("write"); socket_nbd_write(params->client, params->from, params->len, params->data_fd, NULL); diff --git a/src/readwrite.h b/src/readwrite.h index 6b0d3c4..2dabbfc 100644 --- a/src/readwrite.h +++ b/src/readwrite.h @@ -2,7 +2,7 @@ #define __READWRITE_H -int socket_connect(struct sockaddr* to); +int socket_connect(struct sockaddr* to, struct sockaddr* from); off64_t socket_nbd_read_hello(int fd); void socket_nbd_read(int fd, off64_t from, int len, int out_fd, void* out_buf); void socket_nbd_write(int fd, off64_t from, int len, int out_fd, void* out_buf); diff --git a/src/serve.h b/src/serve.h index 1f5bc05..bc356cb 100644 --- a/src/serve.h +++ b/src/serve.h @@ -88,6 +88,7 @@ void server_dirty(struct server *serve, off64_t from, int len); struct mode_readwrite_params { union mysockaddr connect_to; + union mysockaddr connect_from; off64_t from; off64_t len; int data_fd;