Similar to --no-sr, it can be handy for testing a client
implementation to have a server that can easily be forced into older
behaviors, without having to recompile a one-off hack into a server or
dig up an older server binary that lacked a newer feature.
To see the patch in action, try things like:
$ ./nbdkit -U - -fv --mask-handshake=0 null \
--run 'qemu-nbd --list -k $unixsocket'
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
docs/nbdkit-protocol.pod | 25 ++++++++++++++++++-----
docs/synopsis.txt | 2 +-
server/internal.h | 1 +
server/options.h | 2 ++
server/main.c | 30 ++++++++++++++++++----------
server/protocol-handshake-newstyle.c | 4 ++--
6 files changed, 45 insertions(+), 19 deletions(-)
diff --git a/docs/nbdkit-protocol.pod b/docs/nbdkit-protocol.pod
index 3ae89063..272f4e5b 100644
--- a/docs/nbdkit-protocol.pod
+++ b/docs/nbdkit-protocol.pod
@@ -4,8 +4,8 @@ nbdkit - which parts of the NBD protocol nbdkit supports
=head1 SYNOPSIS
- nbdkit [-n|--newstyle] [--no-sr] [-o|--oldstyle] [-e|--exportname EXPORTNAME]
- [...]
+ nbdkit [-n|--newstyle] [--mask-handshake MASK] [--no-sr] [-o|--oldstyle]
+ [-e|--exportname EXPORTNAME] [...]
=head1 DESCRIPTION
@@ -24,15 +24,30 @@ newstyle protocol is better in every respect than the oldstyle
protocol and you should prefer it if possible. The newstyle protocol
also includes an extension where a client may request structured
replies for even more capabilities, such as sparse reads or obtaining
-block status.
+block status. By default, nbdkit advertises as many features as it
+can support (in some cases, this can be limited by what callbacks the
+plugin handles), even if the client does not negotiate to use all
+advertised features.
Use the I<-e> or I<--exportname> flag to set the optional exportname
for the newstyle protocol.
-Use the I<--no-sr> flag to force the newstyle protocol to decline any
-client request for structured replies.
+Nbdkit also includes some options that are useful mainly when
+performing integration tests, for proving whether clients have sane
+fallback behavior when dealing various older servers permitted by the
+NBD protocol. Use the I<--no-sr> flag to force the newstyle protocol
+to decline any client request for structured replies. Use the
+I<--mask-handshake> parameter to mask off particular global features
+which are advertised during new-style handshake (defaulting to all
+supported bits set). Clearing bit 0 (the low order bit) limits a
+client to using just C<NBD_OPT_EXPORT_NAME> (and is incompatible with
+TLS or structured replies); clearing bit 1 causes the handshake to
+send more padding bytes in response to C<NBD_OPT_EXPORT_NAME>. Other
+bits in the mask will only have an effect if the NBD protocol is
+extended in the future to define other global bits.
Use the I<-o> or I<--oldstyle> flag to force the oldstyle protocol.
+In this mode, I<--no-sr> and I<--mask-handshake> have no effect.
=head2 Common clients and the protocol they require
diff --git a/docs/synopsis.txt b/docs/synopsis.txt
index 04cd136d..5fc57fd1 100644
--- a/docs/synopsis.txt
+++ b/docs/synopsis.txt
@@ -3,7 +3,7 @@ nbdkit [-D|--debug PLUGIN|FILTER.FLAG=N]
[--filter FILTER ...] [-f|--foreground]
[-g|--group GROUP] [-i|--ipaddr IPADDR]
[--log stderr|syslog]
- [-n|--newstyle] [--no-sr] [-o|--oldstyle]
+ [-n|--newstyle] [--mask-handshake MASK] [--no-sr] [-o|--oldstyle]
[-P|--pidfile PIDFILE]
[-p|--port PORT] [-r|--readonly]
[--run CMD] [-s|--single] [--selinux-label LABEL]
diff --git a/server/internal.h b/server/internal.h
index 9314e8ff..5da3e3c3 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -90,6 +90,7 @@ extern const char *exportname;
extern bool foreground;
extern const char *ipaddr;
extern enum log_to log_to;
+extern unsigned mask_handshake;
extern bool newstyle;
extern bool no_sr;
extern const char *port;
diff --git a/server/options.h b/server/options.h
index a69f413a..c74e0b8b 100644
--- a/server/options.h
+++ b/server/options.h
@@ -46,6 +46,7 @@ enum {
FILTER_OPTION,
LOG_OPTION,
LONG_OPTIONS_OPTION,
+ MASK_HANDSHAKE_OPTION,
NO_SR_OPTION,
RUN_OPTION,
SELINUX_LABEL_OPTION,
@@ -74,6 +75,7 @@ static const struct option long_options[] = {
{ "ipaddr", required_argument, NULL, 'i' },
{ "log", required_argument, NULL, LOG_OPTION },
{ "long-options", no_argument, NULL, LONG_OPTIONS_OPTION },
+ { "mask-handshake", required_argument, NULL, MASK_HANDSHAKE_OPTION },
{ "new-style", no_argument, NULL, 'n' },
{ "newstyle", no_argument, NULL, 'n' },
{ "no-sr", no_argument, NULL, NO_SR_OPTION },
diff --git a/server/main.c b/server/main.c
index 22cf8d33..d433c1fa 100644
--- a/server/main.c
+++ b/server/main.c
@@ -67,6 +67,7 @@ const char *exportname; /* -e */
bool foreground; /* -f */
const char *ipaddr; /* -i */
enum log_to log_to = LOG_TO_DEFAULT; /* --log */
+unsigned mask_handshake = ~0U; /* --mask-handshake */
bool newstyle = true; /* false = -o, true = -n */
bool no_sr; /* --no-sr */
char *pidfile; /* -P */
@@ -148,6 +149,7 @@ main (int argc, char *argv[])
} *filter_filenames = NULL;
size_t i;
const char *magic_config_key;
+ char *end;
/* Refuse to run if stdin/out/err are closed, whether or not -s is used. */
if (fcntl (STDERR_FILENO, F_GETFL) == -1) {
@@ -348,6 +350,16 @@ main (int argc, char *argv[])
ipaddr = optarg;
break;
+ case MASK_HANDSHAKE_OPTION:
+ errno = 0;
+ mask_handshake = strtoul (optarg, &end, 0);
+ if (errno || *end) {
+ fprintf (stderr, "%s: cannot parse '%s' into
mask-handshake\n",
+ program_name, optarg);
+ exit (EXIT_FAILURE);
+ }
+ break;
+
case 'n':
newstyle = true;
break;
@@ -389,18 +401,14 @@ main (int argc, char *argv[])
break;
case 't':
- {
- char *end;
-
- errno = 0;
- threads = strtoul (optarg, &end, 0);
- if (errno || *end) {
- fprintf (stderr, "%s: cannot parse '%s' into threads\n",
- program_name, optarg);
- exit (EXIT_FAILURE);
- }
- /* XXX Worth a maximimum limit on threads? */
+ errno = 0;
+ threads = strtoul (optarg, &end, 0);
+ if (errno || *end) {
+ fprintf (stderr, "%s: cannot parse '%s' into threads\n",
+ program_name, optarg);
+ exit (EXIT_FAILURE);
}
+ /* XXX Worth a maximimum limit on threads? */
break;
case 'U':
diff --git a/server/protocol-handshake-newstyle.c b/server/protocol-handshake-newstyle.c
index 486d416f..9801f7c2 100644
--- a/server/protocol-handshake-newstyle.c
+++ b/server/protocol-handshake-newstyle.c
@@ -673,7 +673,7 @@ protocol_handshake_newstyle (struct connection *conn)
struct new_handshake handshake;
uint16_t gflags;
- gflags = NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES;
+ gflags = (NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES) & mask_handshake;
debug ("newstyle negotiation: flags: global 0x%x", gflags);
@@ -694,7 +694,7 @@ protocol_handshake_newstyle (struct connection *conn)
/* ... which we check for accuracy. */
debug ("newstyle negotiation: client flags: 0x%x", conn->cflags);
if (conn->cflags & ~gflags) {
- nbdkit_error ("client requested unknown flags 0x%x", conn->cflags);
+ nbdkit_error ("client requested unexpected flags 0x%x", conn->cflags);
return -1;
}
--
2.21.0