flexnbd: Split the proxy mode out into its own binary.
"flexnbd-proxy ..." should be identical in operation to "flexnbd proxy ..."
This commit is contained in:
182
README.proxy.txt
Normal file
182
README.proxy.txt
Normal file
@@ -0,0 +1,182 @@
|
||||
FLEXNBD-PROXY(1)
|
||||
================
|
||||
:doctype: manpage
|
||||
|
||||
NAME
|
||||
----
|
||||
|
||||
flexnbd-proxy - A simple NBD proxy
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
*flexnbd-proxy* ['OPTIONS']
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
flexnbd-proxy is a simple NBD proxy server that implements resilient
|
||||
connection logic for the client. It connects to an upstream NBD server
|
||||
and allows a single client to connect to it. All server properties are
|
||||
proxied to the client, and the client connection is kept alive across
|
||||
reconnections to the upstream server. If the upstream goes away while
|
||||
an NBD request is in-flight then the proxy (silently, from the point
|
||||
of view of the client) reconnects and retransmits the request, before
|
||||
returning the response to the client.
|
||||
|
||||
USAGE
|
||||
-----
|
||||
|
||||
$ 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.
|
||||
|
||||
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
|
||||
it fails, then the process will die with an error exit status.
|
||||
|
||||
Assuming a successful connection to the `upstream` server is made, the proxy
|
||||
will then start listening on the address specified by --addr and --port, waiting
|
||||
for `downstream` to connect to it (this will be your NBD client). The client
|
||||
will be given the same hello message as the proxy was given by the server.
|
||||
|
||||
When connected, any request the client makes will be read by the proxy and sent
|
||||
to the server. If the server goes away for any reason, the proxy will remember
|
||||
the request and regularly (~ every 5 seconds) try to reconnect to the server.
|
||||
Upon reconnection, the request is sent and a reply is waited for. When a reply
|
||||
is received, it is sent back to the client.
|
||||
|
||||
When the client disconnects, cleanly or otherwise, the proxy goes back to
|
||||
waiting for a new client to connect. The connection to the server is maintained
|
||||
at that point, in case it is needed again.
|
||||
|
||||
Only one request may be in-flight at a time under the current architecture; that
|
||||
doesn't seem to slow things down much relative to alternative options, but may
|
||||
be changed in the future if it becomes an issue.
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
*--addr, -l ADDR*:
|
||||
The address to listen on. Required.
|
||||
|
||||
*--port, -p PORT*:
|
||||
The port to listen on. Required.
|
||||
|
||||
*--conn-addr, -C ADDR*:
|
||||
The address of the NBD server to connect to. Required.
|
||||
|
||||
*--conn-port, -P PORT*:
|
||||
The port of the NBD server to connect to. Required.
|
||||
|
||||
*--help, -h* :
|
||||
Show command or global help.
|
||||
|
||||
*--verbose, -v* :
|
||||
Output all available log information to STDERR.
|
||||
|
||||
*--quiet, -q* :
|
||||
Output as little log information as possible to STDERR.
|
||||
|
||||
|
||||
LOGGING
|
||||
-------
|
||||
Log output is sent to STDERR. If --quiet is set, no output will be seen
|
||||
unless the program termintes abnormally. If neither --quiet nor
|
||||
--verbose are set, no output will be seen unless something goes wrong
|
||||
with a specific request. If --verbose is given, every available log
|
||||
message will be seen (which, for a debug build, is many). It is not an
|
||||
error to set both --verbose and --quiet. The last one wins.
|
||||
|
||||
The log line format is:
|
||||
|
||||
<LEVEL>:<PID> <THREAD> <SOURCEFILE>:<SOURCELINE>: <MSG>
|
||||
|
||||
*LEVEL*:
|
||||
This will be one of 'D', 'I', 'W', 'E', 'F' in increasing order of
|
||||
severity. If flexnbd is started with the --quiet flag, only 'F' will be
|
||||
seen. If it is started with the --verbose flag, any from 'I' upwards
|
||||
will be seen. Only if you have a debug build and start it with
|
||||
--verbose will you see 'D' entries.
|
||||
|
||||
*PID*:
|
||||
This is the process ID.
|
||||
|
||||
*THREAD*:
|
||||
flexnbd-proxy is currently single-threaded, so this should be the same
|
||||
for all lines. That may not be the case in the future.
|
||||
|
||||
*SOURCEFILE:SOURCELINE*:
|
||||
Identifies where in the source code this log line can be found.
|
||||
|
||||
*MSG*:
|
||||
A short message describing what's happening, how it's being done, or
|
||||
if you're very lucky *why* it's going on.
|
||||
|
||||
Proxying
|
||||
~~~~~~~~
|
||||
|
||||
The main point of the proxy mode is to allow clients that would otherwise break
|
||||
when the NBD server goes away (during a migration, for instance) to see a
|
||||
persistent TCP connection throughout the process, instead of needing its own
|
||||
reconnection logic.
|
||||
|
||||
For maximum reliability, the proxy process would be run on the same machine as
|
||||
the actual NBD client; an example might look like:
|
||||
|
||||
nbd-server-1$ flexnbd serve -l 10.0.0.1 -p 4777 myfile [...]
|
||||
|
||||
nbd-client-1$ flexnbd-proxy -l 127.0.0.1 -p 4777 -C 10.0.0.1 -P 4777
|
||||
nbd-client-1$ nbd-client -c 127.0.0.1 4777 /dev/nbd0
|
||||
|
||||
nbd-server-2$ flexnbd listen -l 10.0.0.2 -p 4777 -f myfile [...]
|
||||
|
||||
nbd-server-1$ flexnbd mirror --addr 10.0.0.2 -p 4777 [...]
|
||||
|
||||
Upon completing the migration, the mirroring and listening flexnbd servers will
|
||||
both exit. With the proxy mediating requests, this does not break the TCP
|
||||
connection that nbd-client is holding open. If no requests are in-flight, it
|
||||
will not notice anything at all; if requests are in-flight, then the reply may
|
||||
take longer than usual to be returned.
|
||||
|
||||
When flexnbd is restarted in serve mode on the second server:
|
||||
|
||||
nbd-server-2$ flexnbd serve -l 10.0.0.1 -p 4777 -f myfile [...]
|
||||
|
||||
The proxy notices and reconnects, fulfiling any request it has in its buffer.
|
||||
The data in myfile has been moved between physical servers without the nbd
|
||||
client process having to be disturbed at all.
|
||||
|
||||
BUGS
|
||||
----
|
||||
|
||||
Should be reported to nick@bytemark.co.uk.
|
||||
|
||||
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
|
||||
* 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>
|
||||
|
||||
COPYING
|
||||
-------
|
||||
|
||||
Copyright (c) 2012 Bytemark Hosting Ltd. Free use of this software is
|
||||
granted under the terms of the GNU General Public License version 3 or
|
||||
later.
|
||||
|
83
README.txt
83
README.txt
@@ -85,55 +85,6 @@ Options
|
||||
^^^^^^^
|
||||
As for 'serve'.
|
||||
|
||||
proxy
|
||||
~~~~~
|
||||
|
||||
$ flexnbd proxy --addr <ADDR> --port <PORT>
|
||||
--conn-addr <ADDR> --conn-port <PORT> [--bind <ADDR>] [global 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.
|
||||
|
||||
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
|
||||
it fails, then the process will die with an error exit status.
|
||||
|
||||
Assuming a successful connection to the `upstream` server is made, the proxy
|
||||
will then start listening on the address specified by --addr and --port, waiting
|
||||
for `downstream` to connect to it (this will be your NBD client). The client
|
||||
will be given the same hello message as the proxy was given by the server.
|
||||
|
||||
When connected, any request the client makes will be read by the proxy and sent
|
||||
to the server. If the server goes away for any reason, the proxy will remember
|
||||
the request and regularly (~ every 5 seconds) try to reconnect to the server.
|
||||
Upon reconnection, the request is sent and a reply is waited for. When a reply
|
||||
is received, it is sent back to the client.
|
||||
|
||||
When the client disconnects, cleanly or otherwise, the proxy goes back to
|
||||
waiting for a new client to connect. The connection to the server is maintained
|
||||
at that point, in case it is needed again.
|
||||
|
||||
Only one request may be in-flight at a time under the current architecture; that
|
||||
doesn't seem to slow things down much relative to alternative options, but may
|
||||
be changed in the future if it becomes an issue.
|
||||
|
||||
Options
|
||||
^^^^^^^
|
||||
|
||||
*--addr, -l ADDR*:
|
||||
The address to listen on. Required.
|
||||
|
||||
*--port, -p PORT*:
|
||||
The port to listen on. Required.
|
||||
|
||||
*--conn-addr, -C ADDR*:
|
||||
The address of the NBD server to connect to. Required.
|
||||
|
||||
*--conn-port, -P PORT*:
|
||||
The port of the NBD server to connect to. Required.
|
||||
|
||||
mirror
|
||||
~~~~~~
|
||||
|
||||
@@ -415,40 +366,6 @@ Note that because the file is so small in this case, we see the source
|
||||
server quit soon after we start the migration, and the destination
|
||||
exited at roughly the same time.
|
||||
|
||||
Proxying
|
||||
~~~~~~~~
|
||||
|
||||
The main point of the proxy mode is to allow clients that would otherwise break
|
||||
when the NBD server goes away (during a migration, for instance) to see a
|
||||
persistent TCP connection throughout the process, instead of needing its own
|
||||
reconnection logic.
|
||||
|
||||
For maximum reliability, the proxy process would be run on the same machine as
|
||||
the actual NBD client; an example might look like:
|
||||
|
||||
nbd-server-1$ flexnbd serve -l 10.0.0.1 -p 4777 myfile [...]
|
||||
|
||||
nbd-client-1$ flexnbd proxy -l 127.0.0.1 -p 4777 -C 10.0.0.1 -P 4777
|
||||
nbd-client-1$ nbd-client -c 127.0.0.1 4777 /dev/nbd0
|
||||
|
||||
nbd-server-2$ flexnbd listen -l 10.0.0.2 -p 4777 -f myfile [...]
|
||||
|
||||
nbd-server-1$ flexnbd mirror --addr 10.0.0.2 -p 4777 [...]
|
||||
|
||||
Upon completing the migration, the mirroring and listening flexnbd servers will
|
||||
both exit. With the proxy mediating requests, this does not break the TCP
|
||||
connection that nbd-client is holding open. If no requests are in-flight, it
|
||||
will not notice anything at all; if requests are in-flight, then the reply will
|
||||
take longer than usual to be returned.
|
||||
|
||||
When flexnbd is restarted in serve mode on the second server:
|
||||
|
||||
nbd-server-2$ flexnbd serve -l 10.0.0.1 -p 4777 -f myfile [...]
|
||||
|
||||
The proxy notices and reconnects, fulfiling any request it has in its buffer.
|
||||
The data in myfile has been moved between physical servers without the nbd
|
||||
client process having to be disturbed at all.
|
||||
|
||||
BUGS
|
||||
----
|
||||
|
||||
|
62
Rakefile
62
Rakefile
@@ -7,9 +7,17 @@ CC=ENV['CC'] || "gcc"
|
||||
DEBUG = ENV.has_key?('DEBUG') &&
|
||||
%w|yes y ok 1 true t|.include?(ENV['DEBUG'])
|
||||
|
||||
ALL_SOURCES =FileList['src/*']
|
||||
SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ }
|
||||
OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" )
|
||||
ALL_SOURCES = FileList['src/*']
|
||||
|
||||
PROXY_ONLY_SOURCES = FileList['src/{proxy-main,proxy}.c']
|
||||
PROXY_ONLY_OBJECTS = PROXY_ONLY_SOURCES.pathmap( "%{^src,build}X.o" )
|
||||
|
||||
SOURCES = ALL_SOURCES.select { |c| c =~ /\.c$/ } - PROXY_ONLY_SOURCES
|
||||
OBJECTS = SOURCES.pathmap( "%{^src,build}X.o" ) - PROXY_ONLY_OBJECTS
|
||||
|
||||
PROXY_SOURCES = FileList['src/{ioutil,nbdtypes,readwrite,sockutil,util,parse}.c'] + PROXY_ONLY_SOURCES
|
||||
PROXY_OBJECTS = PROXY_SOURCES.pathmap( "%{^src,build}X.o" )
|
||||
|
||||
TEST_SOURCES = FileList['tests/unit/*.c']
|
||||
TEST_OBJECTS = TEST_SOURCES.pathmap( "%{^tests/unit,build/tests}X.o" )
|
||||
|
||||
@@ -38,26 +46,38 @@ if DEBUG
|
||||
end
|
||||
|
||||
desc "Build the binary and man page"
|
||||
task :build => ['build/flexnbd', 'build/flexnbd.1.gz']
|
||||
task :build => [:flexnbd, :flexnbd_proxy, :man]
|
||||
task :default => :build
|
||||
|
||||
desc "Build just the binary"
|
||||
desc "Build just the flexnbd binary"
|
||||
task :flexnbd => "build/flexnbd"
|
||||
|
||||
desc "Build just the flexnbd-proxy binary"
|
||||
task :flexnbd_proxy => "build/flexnbd-proxy"
|
||||
|
||||
def check(m)
|
||||
"build/tests/check_#{m}"
|
||||
end
|
||||
|
||||
file "README.txt"
|
||||
file "README.proxy.txt"
|
||||
|
||||
def manpage(name, src)
|
||||
FileUtils.mkdir_p( "build" )
|
||||
sh "a2x --destination-dir build --format manpage #{src}"
|
||||
sh "gzip -f build/#{name}"
|
||||
end
|
||||
|
||||
file "build/flexnbd.1.gz" => "README.txt" do
|
||||
FileUtils.mkdir_p( "build" )
|
||||
sh "a2x --destination-dir build --format manpage README.txt"
|
||||
sh "gzip -f build/flexnbd.1"
|
||||
manpage("flexnbd.1", "README.txt")
|
||||
end
|
||||
|
||||
file "build/flexnbd-proxy.1.gz" => "README.proxy.txt" do
|
||||
manpage("flexnbd-proxy.1", "README.proxy.txt")
|
||||
end
|
||||
|
||||
desc "Build just the man page"
|
||||
task :man => "build/flexnbd.1.gz"
|
||||
task :man => ["build/flexnbd.1.gz", "build/flexnbd-proxy.1.gz"]
|
||||
|
||||
|
||||
namespace "test" do
|
||||
@@ -83,7 +103,7 @@ namespace "test" do
|
||||
end
|
||||
|
||||
desc "Run NBD test scenarios"
|
||||
task 'scenarios' => 'flexnbd' do
|
||||
task 'scenarios' => ['build/flexnbd', 'build/flexnbd-proxy'] do
|
||||
sh "cd tests/acceptance; ruby nbd_scenarios -v"
|
||||
end
|
||||
end
|
||||
@@ -109,6 +129,10 @@ def headers(c)
|
||||
`#{CC} -Isrc -MM #{c}`.gsub("\\\n", " ").split(" ")[2..-1]
|
||||
end
|
||||
|
||||
rule 'build/flexnbd-proxy' => PROXY_OBJECTS do |t|
|
||||
gcc_link(t.name, t.sources)
|
||||
end
|
||||
|
||||
rule 'build/flexnbd' => OBJECTS do |t|
|
||||
gcc_link(t.name, t.sources)
|
||||
end
|
||||
@@ -125,7 +149,6 @@ file check("client") =>
|
||||
build/parse.o
|
||||
build/client.o
|
||||
build/serve.o
|
||||
build/proxy.o
|
||||
build/acl.o
|
||||
build/ioutil.o
|
||||
build/mbox.o
|
||||
@@ -161,7 +184,6 @@ file check("serve") =>
|
||||
build/client.o
|
||||
build/flexthread.o
|
||||
build/serve.o
|
||||
build/proxy.o
|
||||
build/flexnbd.o
|
||||
build/mirror.o
|
||||
build/status.o
|
||||
@@ -179,7 +201,6 @@ file check("readwrite") =>
|
||||
build/client.o
|
||||
build/self_pipe.o
|
||||
build/serve.o
|
||||
build/proxy.o
|
||||
build/parse.o
|
||||
build/acl.o
|
||||
build/flexthread.o
|
||||
@@ -213,22 +234,20 @@ file check("flexnbd") =>
|
||||
build/nbdtypes.o
|
||||
build/readwrite.o
|
||||
build/mirror.o
|
||||
build/serve.o
|
||||
build/proxy.o} do |t|
|
||||
build/serve.o} do |t|
|
||||
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
||||
end
|
||||
|
||||
|
||||
file check("control") =>
|
||||
%w{build/tests/check_control.o} + OBJECTS - ["build/main.o"] do |t|
|
||||
%w{build/tests/check_control.o} + OBJECTS - ["build/main.o", 'build/proxy-main.o', 'build/proxy.o'] do |t|
|
||||
gcc_link t.name, t.prerequisites + [LIBCHECK]
|
||||
end
|
||||
|
||||
|
||||
(TEST_MODULES- %w{control flexnbd acl client serve readwrite util}).each do |m|
|
||||
tgt = "build/tests/check_#{m}.o"
|
||||
maybe_obj_name = "build/#{m}.o"
|
||||
# Take it out in case we're testing util.o or ioutil.o
|
||||
# Take it out in case we're testing one of the utils
|
||||
deps = ["build/ioutil.o", "build/util.o", "build/sockutil.o"] - [maybe_obj_name]
|
||||
|
||||
# Add it back in if it's something we need to compile
|
||||
@@ -244,6 +263,10 @@ OBJECTS.zip( SOURCES ).each do |o,c|
|
||||
file o => [c]+headers(c) do |t| gcc_compile( o, c ) end
|
||||
end
|
||||
|
||||
PROXY_ONLY_OBJECTS.zip( PROXY_ONLY_SOURCES).each do |o, c|
|
||||
file o => [c]+headers(c) do |t| gcc_compile( o, c ) end
|
||||
end
|
||||
|
||||
TEST_OBJECTS.zip( TEST_SOURCES ).each do |o,c|
|
||||
file o => [c] + headers(c) do |t| gcc_compile( o, c ) end
|
||||
end
|
||||
@@ -255,10 +278,9 @@ end
|
||||
|
||||
namespace :pkg do
|
||||
deb do |t|
|
||||
t.code_files = ALL_SOURCES + ["Rakefile", "README.txt"]
|
||||
t.code_files = ALL_SOURCES + ["Rakefile", "README.txt", "README.proxy.txt"]
|
||||
t.pkg_name = "flexnbd"
|
||||
t.generate_changelog!
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
7
debian/flexnbd.install
vendored
7
debian/flexnbd.install
vendored
@@ -1,2 +1,5 @@
|
||||
build/flexnbd usr/bin
|
||||
build/flexnbd.1.gz usr/share/man/man1
|
||||
build/flexnbd usr/bin
|
||||
build/flexnbd-proxy usr/bin
|
||||
build/flexnbd.1.gz usr/share/man/man1
|
||||
build/flexnbd-proxy.1.gz usr/share/man/man1
|
||||
|
||||
|
@@ -128,27 +128,6 @@ struct flexnbd * flexnbd_create_listening(
|
||||
return flexnbd;
|
||||
}
|
||||
|
||||
|
||||
struct flexnbd * flexnbd_create_proxying(
|
||||
char* s_downstream_address,
|
||||
char* s_downstream_port,
|
||||
char* s_upstream_address,
|
||||
char* s_upstream_port,
|
||||
char* s_upstream_bind
|
||||
)
|
||||
{
|
||||
struct flexnbd * flexnbd = xmalloc( sizeof( struct flexnbd ) );
|
||||
flexnbd->proxy = proxy_create(
|
||||
flexnbd,
|
||||
s_downstream_address,
|
||||
s_downstream_port,
|
||||
s_upstream_address,
|
||||
s_upstream_port,
|
||||
s_upstream_bind);
|
||||
flexnbd_create_shared( flexnbd, NULL );
|
||||
return flexnbd;
|
||||
}
|
||||
|
||||
void flexnbd_spawn_control(struct flexnbd * flexnbd )
|
||||
{
|
||||
NULLCHECK( flexnbd );
|
||||
@@ -274,14 +253,3 @@ int flexnbd_serve( struct flexnbd * flexnbd )
|
||||
return success;
|
||||
}
|
||||
|
||||
int flexnbd_proxy( struct flexnbd * flexnbd )
|
||||
{
|
||||
NULLCHECK( flexnbd );
|
||||
int success;
|
||||
|
||||
success = do_proxy( flexnbd->proxy );
|
||||
debug("do_proxy success is %d", success );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@@ -17,9 +17,6 @@ struct flexnbd {
|
||||
*/
|
||||
struct server * serve;
|
||||
|
||||
/* In proxy mode, this is filled instead of serve, above */
|
||||
struct proxier * proxy;
|
||||
|
||||
/* We only have a control object if a control socket name was
|
||||
* passed on the command line.
|
||||
*/
|
||||
@@ -50,14 +47,6 @@ struct flexnbd * flexnbd_create_listening(
|
||||
int acl_entries,
|
||||
char** s_acl_entries );
|
||||
|
||||
struct flexnbd * flexnbd_create_proxying(
|
||||
char* s_downstream_address,
|
||||
char* s_downstream_port,
|
||||
char* s_upstream_address,
|
||||
char* s_upstream_port,
|
||||
char* s_upstream_bind
|
||||
);
|
||||
|
||||
void flexnbd_destroy( struct flexnbd * );
|
||||
enum mirror_state;
|
||||
enum mirror_state flexnbd_get_mirror_state( struct flexnbd * );
|
||||
|
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||
|
119
src/mode.c
119
src/mode.c
@@ -56,30 +56,6 @@ static char listen_help_text[] =
|
||||
VERBOSE_LINE
|
||||
QUIET_LINE;
|
||||
|
||||
static struct option proxy_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_ADDR,
|
||||
GETOPT_PORT,
|
||||
GETOPT_CONNECT_ADDR,
|
||||
GETOPT_CONNECT_PORT,
|
||||
GETOPT_BIND,
|
||||
GETOPT_QUIET,
|
||||
GETOPT_VERBOSE,
|
||||
{0}
|
||||
};
|
||||
static char proxy_short_options[] = "hl:p:C:P:b:" SOPT_QUIET SOPT_VERBOSE;
|
||||
static char proxy_help_text[] =
|
||||
"Usage: flexnbd " CMD_PROXY " <options>\n\n"
|
||||
"Resiliently proxy an NBD connection between client and server\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_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"
|
||||
QUIET_LINE
|
||||
VERBOSE_LINE;
|
||||
|
||||
static struct option read_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_ADDR,
|
||||
@@ -197,7 +173,6 @@ char help_help_text_arr[] =
|
||||
"Commands:\n"
|
||||
"\tflexnbd serve\n"
|
||||
"\tflexnbd listen\n"
|
||||
"\tflexnbd proxy\n"
|
||||
"\tflexnbd read\n"
|
||||
"\tflexnbd write\n"
|
||||
"\tflexnbd acl\n"
|
||||
@@ -416,46 +391,6 @@ void read_break_param( int c, char **sock )
|
||||
}
|
||||
|
||||
|
||||
void read_proxy_param(
|
||||
int c,
|
||||
char **downstream_addr,
|
||||
char **downstream_port,
|
||||
char **upstream_addr,
|
||||
char **upstream_port,
|
||||
char **bind_addr )
|
||||
{
|
||||
switch( c ) {
|
||||
case 'h' :
|
||||
fprintf( stdout, "%s\n", proxy_help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 'l':
|
||||
*downstream_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
*downstream_port = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
*upstream_addr = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
*upstream_port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
*bind_addr = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
log_level = QUIET_LOG_LEVEL;
|
||||
break;
|
||||
case 'v':
|
||||
log_level = VERBOSE_LOG_LEVEL;
|
||||
break;
|
||||
default:
|
||||
exit_err( proxy_help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_status_param( int c, char **sock )
|
||||
{
|
||||
read_sock_param( c, sock, status_help_text );
|
||||
@@ -799,56 +734,6 @@ int mode_status( int argc, char *argv[] )
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mode_proxy( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
struct flexnbd * flexnbd;
|
||||
char *downstream_addr = NULL;
|
||||
char *downstream_port = NULL;
|
||||
char *upstream_addr = NULL;
|
||||
char *upstream_port = NULL;
|
||||
char *bind_addr = NULL;
|
||||
int success;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL );
|
||||
if ( -1 == c ) { break; }
|
||||
read_proxy_param( c,
|
||||
&downstream_addr,
|
||||
&downstream_port,
|
||||
&upstream_addr,
|
||||
&upstream_port,
|
||||
&bind_addr
|
||||
);
|
||||
}
|
||||
|
||||
if ( NULL == downstream_addr || NULL == downstream_port ){
|
||||
fprintf( stderr, "both --addr and --port are 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" );
|
||||
exit_err( proxy_help_text );
|
||||
}
|
||||
|
||||
flexnbd = flexnbd_create_proxying(
|
||||
downstream_addr,
|
||||
downstream_port,
|
||||
upstream_addr,
|
||||
upstream_port,
|
||||
bind_addr
|
||||
);
|
||||
|
||||
info(
|
||||
"Proxying between %s %s (downstream) and %s %s (upstream)",
|
||||
downstream_addr, downstream_port, upstream_addr, upstream_port
|
||||
);
|
||||
|
||||
success = flexnbd_proxy( flexnbd );
|
||||
flexnbd_destroy( flexnbd );
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
int mode_help( int argc, char *argv[] )
|
||||
{
|
||||
char *cmd;
|
||||
@@ -872,8 +757,6 @@ int mode_help( int argc, char *argv[] )
|
||||
help_text = mirror_help_text;
|
||||
} else if ( IS_CMD( CMD_STATUS, cmd ) ) {
|
||||
help_text = status_help_text;
|
||||
} else if ( IS_CMD( CMD_PROXY, cmd ) ) {
|
||||
help_text = proxy_help_text;
|
||||
} else { exit_err( help_help_text ); }
|
||||
}
|
||||
|
||||
@@ -907,8 +790,6 @@ void mode(char* mode, int argc, char **argv)
|
||||
}
|
||||
else if ( IS_CMD( CMD_STATUS, mode ) ) {
|
||||
mode_status( argc, argv );
|
||||
} else if ( IS_CMD( CMD_PROXY, mode ) ) {
|
||||
mode_proxy( argc, argv );
|
||||
}
|
||||
else if ( IS_CMD( CMD_HELP, mode ) ) {
|
||||
mode_help( argc-1, argv+1 );
|
||||
|
@@ -25,7 +25,6 @@ void mode(char* mode, int argc, char **argv);
|
||||
|
||||
#define CMD_SERVE "serve"
|
||||
#define CMD_LISTEN "listen"
|
||||
#define CMD_PROXY "proxy"
|
||||
#define CMD_READ "read"
|
||||
#define CMD_WRITE "write"
|
||||
#define CMD_ACL "acl"
|
||||
|
172
src/proxy-main.c
Normal file
172
src/proxy-main.c
Normal file
@@ -0,0 +1,172 @@
|
||||
#include <signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include "mode.h"
|
||||
#include "util.h"
|
||||
#include "sockutil.h"
|
||||
#include "proxy.h"
|
||||
|
||||
|
||||
static struct option proxy_options[] = {
|
||||
GETOPT_HELP,
|
||||
GETOPT_ADDR,
|
||||
GETOPT_PORT,
|
||||
GETOPT_CONNECT_ADDR,
|
||||
GETOPT_CONNECT_PORT,
|
||||
GETOPT_BIND,
|
||||
GETOPT_QUIET,
|
||||
GETOPT_VERBOSE,
|
||||
{0}
|
||||
};
|
||||
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"
|
||||
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_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"
|
||||
QUIET_LINE
|
||||
VERBOSE_LINE;
|
||||
|
||||
void read_proxy_param(
|
||||
int c,
|
||||
char **downstream_addr,
|
||||
char **downstream_port,
|
||||
char **upstream_addr,
|
||||
char **upstream_port,
|
||||
char **bind_addr )
|
||||
{
|
||||
switch( c ) {
|
||||
case 'h' :
|
||||
fprintf( stdout, "%s\n", proxy_help_text );
|
||||
exit( 0 );
|
||||
break;
|
||||
case 'l':
|
||||
*downstream_addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
*downstream_port = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
*upstream_addr = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
*upstream_port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
*bind_addr = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
log_level = QUIET_LOG_LEVEL;
|
||||
break;
|
||||
case 'v':
|
||||
log_level = VERBOSE_LOG_LEVEL;
|
||||
break;
|
||||
default:
|
||||
exit_err( proxy_help_text );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stolen from flexnbd.c, wil change in the near future so no point DRYing */
|
||||
int build_signal_fd(void)
|
||||
{
|
||||
sigset_t mask;
|
||||
int sfd;
|
||||
|
||||
sigemptyset( &mask );
|
||||
sigaddset( &mask, SIGTERM );
|
||||
sigaddset( &mask, SIGQUIT );
|
||||
sigaddset( &mask, SIGINT );
|
||||
|
||||
FATAL_UNLESS( 0 == pthread_sigmask( SIG_BLOCK, &mask, NULL ),
|
||||
"Signal blocking failed" );
|
||||
|
||||
sfd = signalfd( -1, &mask, 0 );
|
||||
FATAL_IF( -1 == sfd, "Failed to get a signal fd" );
|
||||
|
||||
return sfd;
|
||||
}
|
||||
|
||||
struct proxier* flexnbd_create_proxying(
|
||||
int signal_fd,
|
||||
char* s_downstream_address,
|
||||
char* s_downstream_port,
|
||||
char* s_upstream_address,
|
||||
char* s_upstream_port,
|
||||
char* s_upstream_bind
|
||||
)
|
||||
{
|
||||
struct proxier* proxy = proxy_create(
|
||||
signal_fd,
|
||||
s_downstream_address,
|
||||
s_downstream_port,
|
||||
s_upstream_address,
|
||||
s_upstream_port,
|
||||
s_upstream_bind
|
||||
);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
int c;
|
||||
struct proxier * proxy;
|
||||
char *downstream_addr = NULL;
|
||||
char *downstream_port = NULL;
|
||||
char *upstream_addr = NULL;
|
||||
char *upstream_port = NULL;
|
||||
char *bind_addr = NULL;
|
||||
int signal_fd;
|
||||
int success;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN); /* calls to splice() unhelpfully throw this */
|
||||
error_init();
|
||||
|
||||
while (1) {
|
||||
c = getopt_long( argc, argv, proxy_short_options, proxy_options, NULL );
|
||||
if ( -1 == c ) { break; }
|
||||
read_proxy_param( c,
|
||||
&downstream_addr,
|
||||
&downstream_port,
|
||||
&upstream_addr,
|
||||
&upstream_port,
|
||||
&bind_addr
|
||||
);
|
||||
}
|
||||
|
||||
if ( NULL == downstream_addr || NULL == downstream_port ){
|
||||
fprintf( stderr, "both --addr and --port are 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" );
|
||||
exit_err( proxy_help_text );
|
||||
}
|
||||
|
||||
signal_fd = build_signal_fd();
|
||||
|
||||
proxy = flexnbd_create_proxying(
|
||||
signal_fd,
|
||||
downstream_addr,
|
||||
downstream_port,
|
||||
upstream_addr,
|
||||
upstream_port,
|
||||
bind_addr
|
||||
);
|
||||
|
||||
info(
|
||||
"Proxying between %s %s (downstream) and %s %s (upstream)",
|
||||
downstream_addr, downstream_port, upstream_addr, upstream_port
|
||||
);
|
||||
|
||||
success = do_proxy( proxy );
|
||||
sock_try_close( signal_fd );
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
15
src/proxy.c
15
src/proxy.c
@@ -11,18 +11,16 @@
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
struct proxier* proxy_create(
|
||||
struct flexnbd* flexnbd,
|
||||
int signal_fd,
|
||||
char* s_downstream_address,
|
||||
char* s_downstream_port,
|
||||
char* s_upstream_address,
|
||||
char* s_upstream_port,
|
||||
char* s_upstream_bind )
|
||||
{
|
||||
NULLCHECK( flexnbd );
|
||||
|
||||
struct proxier* out;
|
||||
out = xmalloc( sizeof( struct proxier ) );
|
||||
out->flexnbd = flexnbd;
|
||||
out->signal_fd = signal_fd;
|
||||
|
||||
FATAL_IF_NULL(s_downstream_address, "Listen address not specified");
|
||||
NULLCHECK( s_downstream_address );
|
||||
@@ -172,13 +170,11 @@ int proxy_should_exit( struct proxier* params, fd_set *check_fds, int wait )
|
||||
fd_set internal_fds;
|
||||
fd_set* fds = check_fds;
|
||||
|
||||
int signal_fd = flexnbd_signal_fd( params->flexnbd );
|
||||
|
||||
if ( NULL == check_fds ) {
|
||||
fds = &internal_fds;
|
||||
|
||||
FD_ZERO( fds );
|
||||
FD_SET( signal_fd, fds );
|
||||
FD_SET( params->signal_fd, fds );
|
||||
|
||||
FATAL_IF_NEGATIVE(
|
||||
sock_try_select(FD_SETSIZE, fds, NULL, NULL, &tv),
|
||||
@@ -186,7 +182,7 @@ int proxy_should_exit( struct proxier* params, fd_set *check_fds, int wait )
|
||||
);
|
||||
}
|
||||
|
||||
if ( FD_ISSET( signal_fd, fds ) ) {
|
||||
if ( FD_ISSET( params->signal_fd, fds ) ) {
|
||||
info( "Stop signal received" );
|
||||
return 1;
|
||||
}
|
||||
@@ -410,7 +406,6 @@ int proxy_accept( struct proxier* params )
|
||||
NULLCHECK( params );
|
||||
|
||||
int client_fd;
|
||||
int signal_fd = flexnbd_signal_fd( params->flexnbd );
|
||||
fd_set fds;
|
||||
int should_continue = 1;
|
||||
|
||||
@@ -421,7 +416,7 @@ int proxy_accept( struct proxier* params )
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(params->listen_fd, &fds);
|
||||
FD_SET(signal_fd, &fds);
|
||||
FD_SET(params->signal_fd, &fds);
|
||||
|
||||
FATAL_IF_NEGATIVE(
|
||||
sock_try_select(FD_SETSIZE, &fds, NULL, NULL, NULL),
|
||||
|
@@ -55,10 +55,13 @@ struct proxier {
|
||||
|
||||
/* We transform the raw reply header into here */
|
||||
struct nbd_reply rsp_hdr;
|
||||
|
||||
/* File descriptor that signal handlers write to */
|
||||
int signal_fd;
|
||||
};
|
||||
|
||||
struct proxier* proxy_create(
|
||||
struct flexnbd * flexnbd,
|
||||
int signal_fd,
|
||||
char* s_downstream_address,
|
||||
char* s_downstream_port,
|
||||
char* s_upstream_address,
|
||||
|
@@ -242,7 +242,7 @@ module FlexNBD
|
||||
end
|
||||
|
||||
def proxy_cmd( connect_ip, connect_port )
|
||||
"#{bin} proxy "\
|
||||
"#{bin}-proxy "\
|
||||
"--addr #{ip} "\
|
||||
"--port #{port} "\
|
||||
"--conn-addr #{connect_ip} "\
|
||||
|
Reference in New Issue
Block a user