nbd-server reports 'negotiation failed' after 'nbd-client -l', and fails on next...
authorRogier <rogier777@gmail.com>
Wed, 30 Jan 2013 18:38:19 +0000 (19:38 +0100)
committerWouter Verhelst <w@uter.be>
Wed, 6 Mar 2013 10:28:34 +0000 (11:28 +0100)
When a client requests an export list from the server,
the latter issues the message 'Error: negotiation failed'.
This is rather confusing, as the only 'problem' is (AFAICT)
that the negotiation phase did everything.

On a subsequent attempt to connect a block device, the server
fails altogether:
Client output:
--------------------
nasc0:root ~ 216 # /tmp/nbd-client.master -l nass0
Negotiation: ..
nasc0-nbd0
nasc0:root ~ 217 # /tmp/nbd-client.master nass0 -N nasc0-nbd0 /dev/nbd0
Negotiation: ..size = 856941734MBError: Exported device is too big for me. Get 64-bit machine :-(

Exiting.
--------------------
Server output:
--------------------
nass0:root ~ 135 # /tmp/nbd-server.master -d -C /etc/nbd-server/config
Error: negotiation failed
Exiting.
Error: Read failed: Connection reset by peer
Exiting.
--------------------
From a tcpdump, it can be observed that the syslog messages end up
on the client socket, which makes the client choke, naturally.

I tested this for the latest git version, and the same problem
occurs. I have traced the problem to the fact that negotiate(),
in two locations, closes the socket, and returns NULL , after which
serveloop also closes it, actually closing the syslog socket instead.
The next accept() returns that same filedescriptor, which syslog()
still thinks it owns.

Note: I added the message 'Session terminated by client', because
the 'ABORT' might also be intended to be used as an irregular
end-of-session (in the future?), besides indicating a regular
end-of-session after a LIST.

--------------------------------------------

nbd-server.c

index 69ee2a4766bfdb3e60175f8bf94a80b1899956d5..e905281d191c56791979d6ae32e12ee852f34373 100644 (file)
@@ -1532,13 +1532,18 @@ static CLIENT* handle_export_name(uint32_t opt, int net, GArray* servers, uint32
        char* name;
        int i;
 
-       if (read(net, &namelen, sizeof(namelen)) < 0)
+       if (read(net, &namelen, sizeof(namelen)) < 0) {
                err("Negotiation failed/7: %m");
+               return NULL;
+       }
        namelen = ntohl(namelen);
        name = malloc(namelen+1);
        name[namelen]=0;
-       if (read(net, name, namelen) < 0)
+       if (read(net, name, namelen) < 0) {
                err("Negotiation failed/8: %m");
+               free(name);
+               return NULL;
+       }
        for(i=0; i<servers->len; i++) {
                SERVER* serve = &(g_array_index(servers, SERVER, i));
                if(!strcmp(serve->servename, name)) {
@@ -1553,6 +1558,7 @@ static CLIENT* handle_export_name(uint32_t opt, int net, GArray* servers, uint32
                        return client;
                }
        }
+       err("Negotiation failed/8a: Requested export not found");
        free(name);
        return NULL;
 }
@@ -1639,7 +1645,7 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) {
                                err_nonfatal("Negotiation failed/5: %m");
                        magic = ntohll(magic);
                        if(magic != opts_magic) {
-                               close(net);
+                               err_nonfatal("Negotiation failed/5a: magic mismatch");
                                return NULL;
                        }
                        if (read(net, &opt, sizeof(opt)) < 0)
@@ -1664,7 +1670,7 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) {
                        }
                } while((opt != NBD_OPT_EXPORT_NAME) && (opt != NBD_OPT_ABORT));
                if(opt == NBD_OPT_ABORT) {
-                       close(net);
+                       err_nonfatal("Session terminated by client");
                        return NULL;
                }
        }
@@ -2305,7 +2311,6 @@ void serveloop(GArray* servers) {
                                }
                                client = negotiate(net, NULL, servers, NEG_INIT | NEG_MODERN);
                                if(!client) {
-                                       err_nonfatal("negotiation failed");
                                        close(net);
                                        continue;
                                }