2 * Test client to test the NBD server. Doesn't do anything useful, except
3 * checking that the server does, actually, work.
5 * Note that the only 'real' test is to check the client against a kernel. If
6 * it works here but does not work in the kernel, then that's most likely a bug
7 * in this program and/or in nbd-server.
9 * Copyright(c) 2006 Wouter Verhelst
11 * This program is Free Software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation, in version 2.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
20 * You should have received a copy of the GNU General Public License along with
21 * this program; if not, write to the Free Software Foundation, Inc., 51
22 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <sys/types.h>
31 #include <sys/socket.h>
39 #include <netinet/in.h>
42 #define MY_NAME "nbd-tester-client"
46 #include "crypto-gnutls.h"
49 static gchar errstr
[1024];
50 const static int errstr_len
= 1023;
54 static int looseordering
= 0;
56 static gchar
*transactionlog
= "nbd-tester-client.tr";
57 static gchar
*certfile
= NULL
;
58 static gchar
*keyfile
= NULL
;
59 static gchar
*cacertfile
= NULL
;
60 static gchar
*tlshostname
= NULL
;
64 CONNECTION_TYPE_CONNECT
,
65 CONNECTION_TYPE_INIT_PASSWD
,
66 CONNECTION_TYPE_CLISERV
,
71 CONNECTION_CLOSE_PROPERLY
,
72 CONNECTION_CLOSE_FAST
,
78 struct nbd_request req
;
79 struct reqcontext
*next
;
80 struct reqcontext
*prev
;
84 struct reqcontext
*head
;
85 struct reqcontext
*tail
;
111 void rclist_unlink(struct rclist
*l
, struct reqcontext
*p
)
114 struct reqcontext
*prev
= p
->prev
;
115 struct reqcontext
*next
= p
->next
;
117 /* Fix link to previous */
134 /* Add a new list item to the tail */
135 void rclist_addtail(struct rclist
*l
, struct reqcontext
*p
)
141 g_warning("addtail found list tail has a next pointer");
148 g_warning("addtail found no list tail but a list head");
157 void chunklist_unlink(struct chunklist
*l
, struct chunk
*p
)
160 struct chunk
*prev
= p
->prev
;
161 struct chunk
*next
= p
->next
;
163 /* Fix link to previous */
180 /* Add a new list item to the tail */
181 void chunklist_addtail(struct chunklist
*l
, struct chunk
*p
)
187 g_warning("addtail found list tail has a next pointer");
194 g_warning("addtail found no list tail but a list head");
203 /* Add some new bytes to a chunklist */
204 void addbuffer(struct chunklist
*l
, void *data
, uint64_t len
)
207 uint64_t size
= 64 * 1024;
208 struct chunk
*pchunk
;
211 /* First see if there is a current chunk, and if it has space */
212 if (l
->tail
&& l
->tail
->space
) {
213 uint64_t towrite
= len
;
214 if (towrite
> l
->tail
->space
)
215 towrite
= l
->tail
->space
;
216 memcpy(l
->tail
->writeptr
, data
, towrite
);
217 l
->tail
->length
+= towrite
;
218 l
->tail
->space
-= towrite
;
219 l
->tail
->writeptr
+= towrite
;
225 /* We still need to write more, so prepare a new chunk */
226 if ((NULL
== (buf
= malloc(size
)))
228 (pchunk
= calloc(1, sizeof(struct chunk
))))) {
229 g_critical("Out of memory");
233 pchunk
->buffer
= buf
;
234 pchunk
->readptr
= buf
;
235 pchunk
->writeptr
= buf
;
236 pchunk
->space
= size
;
237 chunklist_addtail(l
, pchunk
);
243 /* returns 0 on success, -1 on failure */
244 int writebuffer(int fd
, struct chunklist
*l
)
247 struct chunk
*pchunk
= NULL
;
256 if (!(pchunk
->length
) || !(pchunk
->readptr
)) {
257 chunklist_unlink(l
, pchunk
);
258 free(pchunk
->buffer
);
264 /* OK we have a chunk with some data in */
265 res
= write(fd
, pchunk
->readptr
, pchunk
->length
);
270 pchunk
->length
-= res
;
271 pchunk
->readptr
+= res
;
272 if (!pchunk
->length
) {
273 chunklist_unlink(l
, pchunk
);
274 free(pchunk
->buffer
);
280 #define TEST_WRITE (1<<0)
281 #define TEST_FLUSH (1<<1)
282 #define TEST_EXPECT_ERROR (1<<2)
283 #define TEST_HANDSHAKE (1<<3)
285 int timeval_subtract(struct timeval
*result
, struct timeval
*x
,
288 if (x
->tv_usec
< y
->tv_usec
) {
289 int nsec
= (y
->tv_usec
- x
->tv_usec
) / 1000000 + 1;
290 y
->tv_usec
-= 1000000 * nsec
;
294 if (x
->tv_usec
- y
->tv_usec
> 1000000) {
295 int nsec
= (x
->tv_usec
- y
->tv_usec
) / 1000000;
296 y
->tv_usec
+= 1000000 * nsec
;
300 result
->tv_sec
= x
->tv_sec
- y
->tv_sec
;
301 result
->tv_usec
= x
->tv_usec
- y
->tv_usec
;
303 return x
->tv_sec
< y
->tv_sec
;
306 double timeval_diff_to_double(struct timeval
*x
, struct timeval
*y
)
309 timeval_subtract(&r
, x
, y
);
310 return r
.tv_sec
* 1.0 + r
.tv_usec
/ 1000000.0;
313 static inline int read_all(int f
, void *buf
, size_t len
)
319 if ((res
= read(f
, buf
, len
)) <= 0) {
322 snprintf(errstr
, errstr_len
, "Read failed: %s",
333 static inline int write_all(int f
, void *buf
, size_t len
)
339 if ((res
= write(f
, buf
, len
)) <= 0) {
342 snprintf(errstr
, errstr_len
, "Write failed: %s",
353 static int tlserrout (void *opaque
, const char *format
, va_list ap
) {
354 return vfprintf(stderr
, format
, ap
);
357 #define READ_ALL_ERRCHK(f, buf, len, whereto, errmsg...) if((read_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); goto whereto; }
358 #define READ_ALL_ERR_RT(f, buf, len, whereto, rval, errmsg...) if((read_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); retval = rval; goto whereto; }
360 #define WRITE_ALL_ERRCHK(f, buf, len, whereto, errmsg...) if((write_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); goto whereto; }
361 #define WRITE_ALL_ERR_RT(f, buf, len, whereto, rval, errmsg...) if((write_all(f, buf, len))<=0) { snprintf(errstr, errstr_len, ##errmsg); retval = rval; goto whereto; }
363 int setup_connection_common(int sock
, char *name
, CONNECTION_TYPE ctype
,
364 int *serverflags
, int testflags
)
368 uint64_t mymagic
= (name
? opts_magic
: cliserv_magic
);
370 uint16_t handshakeflags
= 0;
371 uint32_t negotiationflags
= 0;
373 if (ctype
< CONNECTION_TYPE_INIT_PASSWD
)
375 READ_ALL_ERRCHK(sock
, buf
, strlen(INIT_PASSWD
), err
,
376 "Could not read INIT_PASSWD: %s", strerror(errno
));
377 if (strlen(buf
) == 0) {
378 snprintf(errstr
, errstr_len
, "Server closed connection");
381 if (strncmp(buf
, INIT_PASSWD
, strlen(INIT_PASSWD
))) {
382 snprintf(errstr
, errstr_len
, "INIT_PASSWD does not match");
385 if (ctype
< CONNECTION_TYPE_CLISERV
)
387 READ_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
388 "Could not read cliserv_magic: %s", strerror(errno
));
389 tmp64
= ntohll(tmp64
);
390 if (tmp64
!= mymagic
) {
391 strncpy(errstr
, "mymagic does not match", errstr_len
);
394 if (ctype
< CONNECTION_TYPE_FULL
)
397 READ_ALL_ERRCHK(sock
, &size
, sizeof(size
), err
,
398 "Could not read size: %s", strerror(errno
));
401 READ_ALL_ERRCHK(sock
, &flags
, sizeof(uint32_t), err
,
402 "Could not read flags: %s", strerror(errno
));
403 flags
= ntohl(flags
);
404 *serverflags
= flags
;
405 READ_ALL_ERRCHK(sock
, buf
, 124, err
, "Could not read data: %s",
409 /* handshake flags */
410 READ_ALL_ERRCHK(sock
, &handshakeflags
, sizeof(handshakeflags
), err
,
411 "Could not read reserved field: %s", strerror(errno
));
412 handshakeflags
= ntohs(handshakeflags
);
413 /* negotiation flags */
414 if (handshakeflags
& NBD_FLAG_FIXED_NEWSTYLE
)
415 negotiationflags
|= NBD_FLAG_C_FIXED_NEWSTYLE
;
417 snprintf(errstr
, errstr_len
, "Cannot negotiate TLS without NBD_FLAG_FIXED_NEWSTYLE");
420 negotiationflags
= htonl(negotiationflags
);
421 WRITE_ALL_ERRCHK(sock
, &negotiationflags
, sizeof(negotiationflags
), err
,
422 "Could not write reserved field: %s", strerror(errno
));
423 if (testflags
& TEST_HANDSHAKE
) {
424 /* Server must support newstyle for this test */
425 if (!(handshakeflags
& NBD_FLAG_FIXED_NEWSTYLE
)) {
426 strncpy(errstr
, "server does not support handshake", errstr_len
);
434 int plainfd
[2]; // [0] is used by the proxy, [1] is used by NBD
435 tlssession_t
*s
= NULL
;
439 tmp64
= htonll(opts_magic
);
440 WRITE_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
441 "Could not write magic: %s", strerror(errno
));
443 tmp32
= htonl(NBD_OPT_STARTTLS
);
444 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
445 "Could not write option: %s", strerror(errno
));
448 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
449 "Could not write option length: %s", strerror(errno
));
451 READ_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
452 "Could not read cliserv_magic: %s", strerror(errno
));
453 tmp64
= ntohll(tmp64
);
454 if (tmp64
!= NBD_OPT_REPLY_MAGIC
) {
455 strncpy(errstr
, "reply magic does not match", errstr_len
);
458 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
459 "Could not read option type: %s", strerror(errno
));
460 tmp32
= ntohl(tmp32
);
461 if (tmp32
!= NBD_OPT_STARTTLS
) {
462 strncpy(errstr
, "Reply to wrong option", errstr_len
);
465 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
466 "Could not read option reply type: %s", strerror(errno
));
467 tmp32
= ntohl(tmp32
);
468 if (tmp32
!= NBD_REP_ACK
) {
469 if(tmp32
& NBD_REP_FLAG_ERROR
) {
470 snprintf(errstr
, errstr_len
, "Received error %d", tmp32
& ~NBD_REP_FLAG_ERROR
);
472 snprintf(errstr
, errstr_len
, "Option reply type %d != NBD_REP_ACK", tmp32
);
476 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
477 "Could not read option data length: %s", strerror(errno
));
478 tmp32
= ntohl(tmp32
);
480 strncpy(errstr
, "Option reply data length != 0", errstr_len
);
484 s
= tlssession_new(FALSE
,
489 !cacertfile
|| !tlshostname
, // insecure flag
496 tlserrout
, // erroutfn
500 strncpy(errstr
, "Cannot establish TLS session", errstr_len
);
504 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, plainfd
) < 0) {
505 strncpy(errstr
, "Cannot get socket pair", errstr_len
);
509 if (set_nonblocking(plainfd
[0], 0) <0 ||
510 set_nonblocking(plainfd
[1], 0) <0 ||
511 set_nonblocking(sock
, 0) <0) {
514 strncpy(errstr
, "Cannot set socket options", errstr_len
);
520 err("Could not fork");
523 signal (SIGPIPE
, SIG_IGN
);
525 tlssession_mainloop(sock
, plainfd
[0], s
);
532 sock
= plainfd
[1]; /* use the decrypted FD from now on */
536 strncpy(errstr
, "TLS requested but support not compiled in", errstr_len
);
540 if(testflags
& TEST_EXPECT_ERROR
) {
541 struct sigaction act
;
542 memset(&act
, '0', sizeof act
);
543 act
.sa_handler
= SIG_IGN
;
544 sigaction(SIGPIPE
, &act
, NULL
);
547 tmp64
= htonll(opts_magic
);
548 WRITE_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
549 "Could not write magic: %s", strerror(errno
));
551 tmp32
= htonl(NBD_OPT_EXPORT_NAME
);
552 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
553 "Could not write option: %s", strerror(errno
));
554 tmp32
= htonl((uint32_t) strlen(name
));
555 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
556 "Could not write name length: %s", strerror(errno
));
557 WRITE_ALL_ERRCHK(sock
, name
, strlen(name
), err
,
558 "Could not write name:: %s", strerror(errno
));
559 READ_ALL_ERRCHK(sock
, &size
, sizeof(size
), err
,
560 "Could not read size: %s", strerror(errno
));
563 READ_ALL_ERRCHK(sock
, &flags
, sizeof(uint16_t), err
,
564 "Could not read flags: %s", strerror(errno
));
565 flags
= ntohs(flags
);
566 *serverflags
= flags
;
567 READ_ALL_ERRCHK(sock
, buf
, 124, err
,
568 "Could not read reserved zeroes: %s", strerror(errno
));
577 int setup_unix_connection(gchar
* unixsock
, gchar
* name
, CONNECTION_TYPE ctype
,
578 int *serverflags
, int testflags
)
580 struct sockaddr_un addr
;
584 if (ctype
< CONNECTION_TYPE_CONNECT
) {
587 if ((sock
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
588 strncpy(errstr
, strerror(errno
), errstr_len
);
593 memset(&addr
, 0, sizeof(struct sockaddr_un
));
594 addr
.sun_family
= AF_UNIX
;
595 strncpy(addr
.sun_path
, unixsock
, sizeof addr
.sun_path
);
596 addr
.sun_path
[sizeof(addr
.sun_path
)-1] = '\0';
597 if (connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
598 strncpy(errstr
, strerror(errno
), errstr_len
);
601 sock
= setup_connection_common(sock
, name
, ctype
, serverflags
, testflags
);
611 int setup_inet_connection(gchar
* hostname
, int port
, gchar
* name
,
612 CONNECTION_TYPE ctype
, int *serverflags
, int testflags
)
615 struct hostent
*host
;
616 struct sockaddr_in addr
;
619 if (ctype
< CONNECTION_TYPE_CONNECT
)
621 if ((sock
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
)) < 0) {
622 strncpy(errstr
, strerror(errno
), errstr_len
);
626 if (!(host
= gethostbyname(hostname
))) {
627 strncpy(errstr
, hstrerror(h_errno
), errstr_len
);
630 addr
.sin_family
= AF_INET
;
631 addr
.sin_port
= htons(port
);
632 addr
.sin_addr
.s_addr
= *((int *)host
->h_addr
);
633 if ((connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)) {
634 strncpy(errstr
, strerror(errno
), errstr_len
);
637 sock
= setup_connection_common(sock
, name
, ctype
, serverflags
, testflags
);
647 int setup_connection(gchar
* hostname
, gchar
* unixsock
, int port
, gchar
* name
,
648 CONNECTION_TYPE ctype
, int *serverflags
, int testflags
)
650 if (hostname
!= NULL
) {
651 return setup_inet_connection(hostname
, port
, name
, ctype
,
652 serverflags
, testflags
);
653 } else if (unixsock
!= NULL
) {
654 return setup_unix_connection(unixsock
, name
, ctype
,
655 serverflags
, testflags
);
657 g_error("need a hostname or a unix domain socket!");
662 int close_connection(int sock
, CLOSE_TYPE type
)
664 struct nbd_request req
;
668 case CONNECTION_CLOSE_PROPERLY
:
669 req
.magic
= htonl(NBD_REQUEST_MAGIC
);
670 req
.type
= htonl(NBD_CMD_DISC
);
671 memcpy(&(req
.handle
), &(counter
), sizeof(counter
));
675 if (write(sock
, &req
, sizeof(req
)) < 0) {
676 snprintf(errstr
, errstr_len
,
677 "Could not write to socket: %s",
681 case CONNECTION_CLOSE_FAST
:
682 if (close(sock
) < 0) {
683 snprintf(errstr
, errstr_len
,
684 "Could not close socket: %s", strerror(errno
));
689 g_critical("Your compiler is on crack!"); /* or I am buggy */
695 int read_packet_check_header(int sock
, size_t datasize
, long long int curhandle
)
697 struct nbd_reply rep
;
701 READ_ALL_ERR_RT(sock
, &rep
, sizeof(rep
), end
, -1,
702 "Could not read reply header: %s", strerror(errno
));
703 rep
.magic
= ntohl(rep
.magic
);
704 rep
.error
= ntohl(rep
.error
);
705 if (rep
.magic
!= NBD_REPLY_MAGIC
) {
706 snprintf(errstr
, errstr_len
,
707 "Received package with incorrect reply_magic. Index of sent packages is %lld (0x%llX), received handle is %lld (0x%llX). Received magic 0x%lX, expected 0x%lX",
708 (long long int)curhandle
,
709 (long long unsigned int)curhandle
,
710 (long long int)*((u64
*) rep
.handle
),
711 (long long unsigned int)*((u64
*) rep
.handle
),
712 (long unsigned int)rep
.magic
,
713 (long unsigned int)NBD_REPLY_MAGIC
);
718 snprintf(errstr
, errstr_len
,
719 "Received error from server: %ld (0x%lX). Handle is %lld (0x%llX).",
720 (long int)rep
.error
, (long unsigned int)rep
.error
,
721 (long long int)(*((u64
*) rep
.handle
)),
722 (long long unsigned int)*((u64
*) rep
.handle
));
727 READ_ALL_ERR_RT(sock
, &buf
, datasize
, end
, -1,
728 "Could not read data: %s", strerror(errno
));
734 int oversize_test(gchar
* hostname
, gchar
* unixsock
, int port
, char *name
,
735 int sock
, char sock_is_open
, char close_sock
, int testflags
)
738 struct nbd_request req
;
739 struct nbd_reply rep
;
742 pid_t G_GNUC_UNUSED mypid
= getpid();
743 char buf
[((1024 * 1024) + sizeof(struct nbd_request
) / 2) << 1];
746 /* This should work */
749 setup_connection(hostname
, unixsock
, port
, name
,
750 CONNECTION_TYPE_FULL
,
751 &serverflags
, testflags
)) < 0) {
752 g_warning("Could not open socket: %s", errstr
);
757 req
.magic
= htonl(NBD_REQUEST_MAGIC
);
758 req
.type
= htonl(NBD_CMD_READ
);
759 req
.len
= htonl(1024 * 1024);
760 memcpy(&(req
.handle
), &i
, sizeof(i
));
761 req
.from
= htonll(i
);
762 WRITE_ALL_ERR_RT(sock
, &req
, sizeof(req
), err
, -1,
763 "Could not write request: %s", strerror(errno
));
764 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req
.len
));
765 READ_ALL_ERR_RT(sock
, &rep
, sizeof(struct nbd_reply
), err
, -1,
766 "Could not read reply header: %s", strerror(errno
));
767 READ_ALL_ERR_RT(sock
, &buf
, ntohl(req
.len
), err
, -1,
768 "Could not read data: %s", strerror(errno
));
770 snprintf(errstr
, errstr_len
, "Received unexpected error: %d",
777 /* This probably should not work */
779 req
.from
= htonll(i
);
780 req
.len
= htonl(ntohl(req
.len
) + sizeof(struct nbd_request
) / 2);
781 WRITE_ALL_ERR_RT(sock
, &req
, sizeof(req
), err
, -1,
782 "Could not write request: %s", strerror(errno
));
783 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req
.len
));
784 READ_ALL_ERR_RT(sock
, &rep
, sizeof(struct nbd_reply
), err
, -1,
785 "Could not read reply header: %s", strerror(errno
));
786 READ_ALL_ERR_RT(sock
, &buf
, ntohl(req
.len
), err
, -1,
787 "Could not read data: %s", strerror(errno
));
789 printf("Received expected error\n");
795 /* ... unless this works, too */
797 req
.from
= htonll(i
);
798 req
.len
= htonl(ntohl(req
.len
) << 1);
799 WRITE_ALL_ERR_RT(sock
, &req
, sizeof(req
), err
, -1,
800 "Could not write request: %s", strerror(errno
));
801 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req
.len
));
802 READ_ALL_ERR_RT(sock
, &rep
, sizeof(struct nbd_reply
), err
, -1,
803 "Could not read reply header: %s", strerror(errno
));
804 READ_ALL_ERR_RT(sock
, &buf
, ntohl(req
.len
), err
, -1,
805 "Could not read data: %s", strerror(errno
));
811 if ((rep
.error
&& !got_err
) || (!rep
.error
&& got_err
)) {
812 printf("Received unexpected error\n");
819 int handshake_test(gchar
* hostname
, gchar
* unixsock
, int port
, char *name
,
820 int sock
, char sock_is_open
, char close_sock
, int testflags
)
827 /* This should work */
830 setup_connection(hostname
, unixsock
, port
, name
,
831 CONNECTION_TYPE_FULL
,
832 &serverflags
, testflags
)) < 0) {
833 g_warning("Could not open socket: %s", errstr
);
838 /* Intentionally throw an unknown option at the server */
839 tmp64
= htonll(opts_magic
);
840 WRITE_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
841 "Could not write magic: %s", strerror(errno
));
842 tmp32
= htonl(0x7654321);
843 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
844 "Could not write option: %s", strerror(errno
));
845 tmp32
= htonl((uint32_t) sizeof(tmp32
));
846 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
847 "Could not write option length: %s", strerror(errno
));
848 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
849 "Could not write option payload: %s", strerror(errno
));
850 /* Expect proper error from server */
851 READ_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
852 "Could not read magic: %s", strerror(errno
));
853 tmp64
= ntohll(tmp64
);
854 if (tmp64
!= 0x3e889045565a9LL
) {
855 strncpy(errstr
, "magic does not match", errstr_len
);
858 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
859 "Could not read option: %s", strerror(errno
));
860 tmp32
= ntohl(tmp32
);
861 if (tmp32
!= 0x7654321) {
862 strncpy(errstr
, "option does not match", errstr_len
);
865 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
866 "Could not read status: %s", strerror(errno
));
867 tmp32
= ntohl(tmp32
);
868 if (tmp32
!= NBD_REP_ERR_UNSUP
) {
869 strncpy(errstr
, "status does not match", errstr_len
);
872 READ_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
873 "Could not read length: %s", strerror(errno
));
874 tmp32
= ntohl(tmp32
);
877 size_t len
= tmp32
< sizeof(buf
) ? tmp32
: sizeof(buf
);
878 READ_ALL_ERRCHK(sock
, buf
, len
, err
,
879 "Could not read payload: %s", strerror(errno
));
884 /* Send NBD_OPT_ABORT to close the connection */
885 tmp64
= htonll(opts_magic
);
886 WRITE_ALL_ERRCHK(sock
, &tmp64
, sizeof(tmp64
), err
,
887 "Could not write magic: %s", strerror(errno
));
888 tmp32
= htonl(NBD_OPT_ABORT
);
889 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
890 "Could not write option: %s", strerror(errno
));
891 tmp32
= htonl((uint32_t) 0);
892 WRITE_ALL_ERRCHK(sock
, &tmp32
, sizeof(tmp32
), err
,
893 "Could not write option length: %s", strerror(errno
));
897 g_message("Handshake test completed. No errors encountered.");
902 int throughput_test(gchar
* hostname
, gchar
* unixsock
, int port
, char *name
,
903 int sock
, char sock_is_open
, char close_sock
, int testflags
)
907 struct nbd_request req
;
911 struct timeval start
;
915 char speedchar
[2] = { '\0', '\0' };
918 signed int do_write
= TRUE
;
919 pid_t mypid
= getpid();
920 char *print
= getenv("NBD_TEST_SILENT");
922 if (!(testflags
& TEST_WRITE
))
923 testflags
&= ~TEST_FLUSH
;
925 memset(writebuf
, 'X', 1024);
929 setup_connection(hostname
, unixsock
, port
, name
,
930 CONNECTION_TYPE_FULL
,
931 &serverflags
, testflags
)) < 0) {
932 g_warning("Could not open socket: %s", errstr
);
933 if(testflags
& TEST_EXPECT_ERROR
) {
934 g_message("Test failed, as expected");
942 if ((testflags
& TEST_FLUSH
)
943 && ((serverflags
& (NBD_FLAG_SEND_FLUSH
| NBD_FLAG_SEND_FUA
))
944 != (NBD_FLAG_SEND_FLUSH
| NBD_FLAG_SEND_FUA
))) {
945 snprintf(errstr
, errstr_len
,
946 "Server did not supply flush capability flags");
950 req
.magic
= htonl(NBD_REQUEST_MAGIC
);
951 req
.len
= htonl(1024);
952 if (gettimeofday(&start
, NULL
) < 0) {
954 snprintf(errstr
, errstr_len
, "Could not measure start time: %s",
958 for (i
= 0; i
+ 1024 <= size
; i
+= 1024) {
960 int sendfua
= (testflags
& TEST_FLUSH
)
961 && (((i
>> 10) & 15) == 3);
962 int sendflush
= (testflags
& TEST_FLUSH
)
963 && (((i
>> 10) & 15) == 11);
965 htonl((testflags
& TEST_WRITE
) ? NBD_CMD_WRITE
:
969 htonl(NBD_CMD_WRITE
| NBD_CMD_FLAG_FUA
);
970 memcpy(&(req
.handle
), &i
, sizeof(i
));
971 req
.from
= htonll(i
);
972 if (write_all(sock
, &req
, sizeof(req
)) < 0) {
976 if (testflags
& TEST_WRITE
) {
977 if (write_all(sock
, writebuf
, 1024) < 0) {
984 long long int j
= i
^ (1LL << 63);
985 req
.type
= htonl(NBD_CMD_FLUSH
);
986 memcpy(&(req
.handle
), &j
, sizeof(j
));
989 if (write_all(sock
, &req
, sizeof(req
)) < 0) {
993 req
.len
= htonl(1024);
1002 select(sock
+ 1, &set
, NULL
, NULL
, &tv
);
1003 if (FD_ISSET(sock
, &set
)) {
1004 /* Okay, there's something ready for
1008 read_packet_check_header(sock
,
1012 if (!(testflags
& TEST_EXPECT_ERROR
)
1020 if (testflags
& TEST_EXPECT_ERROR
) {
1027 } while (FD_ISSET(sock
, &set
));
1028 /* Now wait until we can write again or until a second have
1029 * passed, whichever comes first*/
1034 do_write
= select(sock
+ 1, NULL
, &set
, NULL
, &tv
);
1036 printf("Select finished\n");
1038 snprintf(errstr
, errstr_len
, "select: %s",
1044 printf("%d: Requests: %d \r", (int)mypid
, requests
);
1047 /* Now empty the read buffer */
1053 select(sock
+ 1, &set
, NULL
, NULL
, &tv
);
1054 if (FD_ISSET(sock
, &set
)) {
1055 /* Okay, there's something ready for
1057 read_packet_check_header(sock
,
1058 (testflags
& TEST_WRITE
) ? 0 :
1063 printf("%d: Requests: %d \r", (int)mypid
, requests
);
1066 printf("%d: Requests: %d \n", (int)mypid
, requests
);
1067 if (gettimeofday(&stop
, NULL
) < 0) {
1069 snprintf(errstr
, errstr_len
, "Could not measure end time: %s",
1073 timespan
= timeval_diff_to_double(&stop
, &start
);
1074 speed
= size
/ timespan
;
1076 speed
= speed
/ 1024.0;
1080 speed
= speed
/ 1024.0;
1084 speed
= speed
/ 1024.0;
1088 ("%d: Throughput %s test (%s flushes) complete. Took %.3f seconds to complete, %.3f%sib/s",
1089 (int)getpid(), (testflags
& TEST_WRITE
) ? "write" : "read",
1090 (testflags
& TEST_FLUSH
) ? "with" : "without", timespan
, speed
,
1095 close_connection(sock
, CONNECTION_CLOSE_PROPERLY
);
1102 * fill 512 byte buffer 'buf' with a hashed selection of interesting data based
1103 * only on handle and blknum. The first word is blknum, and the second handle, for ease
1104 * of understanding. Things with handle 0 are blank.
1106 static inline void makebuf(char *buf
, uint64_t seq
, uint64_t blknum
)
1108 uint64_t x
= ((uint64_t) blknum
) ^ (seq
<< 32) ^ (seq
>> 32);
1109 uint64_t *p
= (uint64_t *) buf
;
1115 for (i
= 0; i
< 512 / sizeof(uint64_t); i
++) {
1118 x
+= 0xFEEDA1ECDEADBEEFULL
+ i
+ (((uint64_t) i
) << 56);
1120 x
= x
^ (x
<< s
) ^ (x
>> (64 - s
)) ^ 0xAA55AA55AA55AA55ULL
^
1125 static inline int checkbuf(char *buf
, uint64_t seq
, uint64_t blknum
)
1127 uint64_t cmp
[64]; // 512/8 = 64
1128 makebuf((char *)cmp
, seq
, blknum
);
1129 return memcmp(cmp
, buf
, 512) ? -1 : 0;
1132 static inline void dumpcommand(char *text
, uint32_t command
)
1134 #ifdef DEBUG_COMMANDS
1135 command
= ntohl(command
);
1137 switch (command
& NBD_CMD_MASK_COMMAND
) {
1139 ctext
= "NBD_CMD_READ";
1142 ctext
= "NBD_CMD_WRITE";
1145 ctext
= "NBD_CMD_DISC";
1148 ctext
= "NBD_CMD_FLUSH";
1154 printf("%s: %s [%s] (0x%08x)\n",
1156 ctext
, (command
& NBD_CMD_FLAG_FUA
) ? "FUA" : "NONE", command
);
1160 /* return an unused handle */
1161 uint64_t getrandomhandle(GHashTable
* phash
)
1163 uint64_t handle
= 0;
1166 /* RAND_MAX may be as low as 2^15 */
1167 for (i
= 1; i
<= 5; i
++)
1168 handle
^= random() ^ (handle
<< 15);
1169 } while (g_hash_table_lookup(phash
, &handle
));
1173 int integrity_test(gchar
* hostname
, gchar
* unixsock
, int port
, char *name
,
1174 int sock
, char sock_is_open
, char close_sock
, int testflags
)
1176 struct nbd_reply rep
;
1180 struct timeval start
;
1181 struct timeval stop
;
1184 char speedchar
[2] = { '\0', '\0' };
1186 int serverflags
= 0;
1187 pid_t G_GNUC_UNUSED mypid
= getpid();
1189 char *blkhashname
= NULL
;
1190 struct blkitem
*blkhash
= NULL
;
1193 uint64_t processed
= 0;
1194 uint64_t printer
= 0;
1195 char *do_print
= getenv("NBD_TEST_SILENT");
1197 int readtransactionfile
= 1;
1199 struct rclist txqueue
= { NULL
, NULL
, 0 };
1200 struct rclist inflight
= { NULL
, NULL
, 0 };
1201 struct chunklist txbuf
= { NULL
, NULL
, 0 };
1203 GHashTable
*handlehash
= g_hash_table_new(g_int64_hash
, g_int64_equal
);
1206 if (!sock_is_open
) {
1208 setup_connection(hostname
, unixsock
, port
, name
,
1209 CONNECTION_TYPE_FULL
,
1210 &serverflags
, testflags
)) < 0) {
1211 g_warning("Could not open socket: %s", errstr
);
1216 if ((serverflags
& (NBD_FLAG_SEND_FLUSH
| NBD_FLAG_SEND_FUA
))
1217 != (NBD_FLAG_SEND_FLUSH
| NBD_FLAG_SEND_FUA
))
1219 ("Server flags do not support FLUSH and FUA - these may error");
1222 blkhashname
= strdup("/tmp/blkarray-XXXXXX");
1223 if (!blkhashname
|| (-1 == (blkhashfd
= mkstemp(blkhashname
)))) {
1224 g_warning("Could not open temp file: %s", strerror(errno
));
1228 /* use tmpnam here to avoid further feature test nightmare */
1229 if (-1 == (blkhashfd
= open(blkhashname
= strdup(tmpnam(NULL
)),
1231 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
))) {
1232 g_warning("Could not open temp file: %s", strerror(errno
));
1236 /* Ensure space freed if we die */
1237 if (-1 == unlink(blkhashname
)) {
1238 g_warning("Could not unlink temp file: %s", strerror(errno
));
1243 lseek(blkhashfd
, (off_t
) ((size
>> 9) * sizeof(struct blkitem
)),
1245 g_warning("Could not llseek temp file: %s", strerror(errno
));
1249 if (-1 == write(blkhashfd
, "\0", 1)) {
1250 g_warning("Could not write temp file: %s", strerror(errno
));
1254 if (NULL
== (blkhash
= mmap(NULL
,
1255 (size
>> 9) * sizeof(struct blkitem
),
1256 PROT_READ
| PROT_WRITE
,
1257 MAP_SHARED
, blkhashfd
, 0))) {
1258 g_warning("Could not mmap temp file: %s", strerror(errno
));
1262 if (-1 == (logfd
= open(transactionlog
, O_RDONLY
))) {
1263 g_warning("Could open log file: %s", strerror(errno
));
1267 if (gettimeofday(&start
, NULL
) < 0) {
1268 snprintf(errstr
, errstr_len
, "Could not measure start time: %s",
1273 while (readtransactionfile
|| txqueue
.numitems
|| txbuf
.numitems
1274 || inflight
.numitems
) {
1281 struct reqcontext
*prc
;
1287 if (readtransactionfile
)
1288 FD_SET(logfd
, &rset
);
1289 if ((!blocked
&& txqueue
.numitems
) || txbuf
.numitems
)
1290 FD_SET(sock
, &wset
);
1291 if (inflight
.numitems
)
1292 FD_SET(sock
, &rset
);
1296 select(1 + ((sock
> logfd
) ? sock
: logfd
), &rset
, &wset
,
1299 snprintf(errstr
, errstr_len
,
1300 "Timeout reading from socket");
1302 } else if (ret
< 0) {
1303 g_warning("Could not mmap temp file: %s", errstr
);
1306 /* We know we've got at least one thing to do here then */
1308 /* Get a command from the transaction log */
1309 if (FD_ISSET(logfd
, &rset
)) {
1311 /* Read a request or reply from the transaction file */
1312 READ_ALL_ERRCHK(logfd
,
1316 "Could not read transaction log: %s",
1318 magic
= ntohl(magic
);
1320 case NBD_REQUEST_MAGIC
:
1323 calloc(1, sizeof(struct reqcontext
)))) {
1324 snprintf(errstr
, errstr_len
,
1325 "Could not allocate request");
1328 READ_ALL_ERRCHK(logfd
,
1330 (char *)&(prc
->req
),
1331 sizeof(struct nbd_request
) -
1332 sizeof(magic
), err_open
,
1333 "Could not read transaction log: %s",
1335 prc
->req
.magic
= htonl(NBD_REQUEST_MAGIC
);
1336 memcpy(prc
->orighandle
, prc
->req
.handle
, 8);
1338 if ((ntohl(prc
->req
.type
) &
1339 NBD_CMD_MASK_COMMAND
) == NBD_CMD_DISC
) {
1340 /* no more to read; don't enqueue as no reply
1341 * we will disconnect manually at the end
1343 readtransactionfile
= 0;
1346 dumpcommand("Enqueuing command",
1348 rclist_addtail(&txqueue
, prc
);
1352 case NBD_REPLY_MAGIC
:
1353 READ_ALL_ERRCHK(logfd
,
1354 sizeof(magic
) + (char *)(&rep
),
1355 sizeof(struct nbd_reply
) -
1356 sizeof(magic
), err_open
,
1357 "Could not read transaction log: %s",
1361 snprintf(errstr
, errstr_len
,
1362 "Transaction log file contained errored transaction");
1366 /* We do not need to consume data on a read reply as there is
1367 * none in the log */
1370 snprintf(errstr
, errstr_len
,
1371 "Could not measure start time: %08x",
1377 /* See if we have a write we can do */
1378 if (FD_ISSET(sock
, &wset
)) {
1379 if ((!(txqueue
.head
) && !(txbuf
.head
)) || blocked
)
1381 ("Socket write FD set but we shouldn't have been interested");
1383 /* If there is no buffered data, generate some */
1384 if (!blocked
&& !(txbuf
.head
)
1385 && (NULL
!= (prc
= txqueue
.head
))) {
1386 if (ntohl(prc
->req
.magic
) != NBD_REQUEST_MAGIC
) {
1388 ("Asked to write a request without a magic number");
1392 command
= ntohl(prc
->req
.type
);
1393 from
= ntohll(prc
->req
.from
);
1394 len
= ntohl(prc
->req
.len
);
1396 /* First check whether we can touch this command at all. If this
1397 * command is a read, and there is an inflight write, OR if this
1398 * command is a write, and there is an inflight read or write, then
1399 * we need to leave the command alone and signal that we are blocked
1402 if (!looseordering
) {
1408 uint64_t blknum
= cfrom
>> 9;
1409 if (cfrom
>= size
) {
1412 "offset %llx beyond size %llx",
1419 if (blkhash
[blknum
].inflightw
||
1420 (blkhash
[blknum
].inflightr
1423 NBD_CMD_MASK_COMMAND
) ==
1436 rclist_unlink(&txqueue
, prc
);
1437 rclist_addtail(&inflight
, prc
);
1439 dumpcommand("Sending command", prc
->req
.type
);
1440 /* we rewrite the handle as they otherwise may not be unique */
1441 *((uint64_t *) (prc
->req
.handle
)) =
1442 getrandomhandle(handlehash
);
1443 g_hash_table_insert(handlehash
, prc
->req
.handle
,
1445 addbuffer(&txbuf
, &(prc
->req
),
1446 sizeof(struct nbd_request
));
1447 switch (command
& NBD_CMD_MASK_COMMAND
) {
1451 uint64_t blknum
= from
>> 9;
1456 "offset %llx beyond size %llx",
1463 (blkhash
[blknum
].inflightw
)++;
1464 /* work out what we should be writing */
1465 makebuf(dbuf
, prc
->seq
, blknum
);
1466 addbuffer(&txbuf
, dbuf
, 512);
1474 uint64_t blknum
= from
>> 9;
1478 "offset %llx beyond size %llx",
1485 (blkhash
[blknum
].inflightr
)++;
1494 snprintf(errstr
, errstr_len
,
1495 "Incomprehensible command: %08x",
1505 /* there should be some now */
1506 if (writebuffer(sock
, &txbuf
) < 0) {
1507 snprintf(errstr
, errstr_len
,
1508 "Failed to write to socket buffer: %s",
1515 /* See if there is a reply to be processed from the socket */
1516 if (FD_ISSET(sock
, &rset
)) {
1517 /* Okay, there's something ready for
1520 READ_ALL_ERRCHK(sock
,
1522 sizeof(struct nbd_reply
),
1524 "Could not read from server socket: %s",
1527 if (rep
.magic
!= htonl(NBD_REPLY_MAGIC
)) {
1528 snprintf(errstr
, errstr_len
,
1529 "Bad magic from server");
1534 snprintf(errstr
, errstr_len
,
1535 "Server errored a transaction");
1540 memcpy(&handle
, rep
.handle
, 8);
1541 prc
= g_hash_table_lookup(handlehash
, &handle
);
1543 snprintf(errstr
, errstr_len
,
1544 "Unrecognised handle in reply: 0x%llX",
1545 *(long long unsigned int *)(rep
.
1549 if (!g_hash_table_remove(handlehash
, &handle
)) {
1550 snprintf(errstr
, errstr_len
,
1551 "Could not remove handle from hash: 0x%llX",
1552 *(long long unsigned int *)(rep
.
1557 if (prc
->req
.magic
!= htonl(NBD_REQUEST_MAGIC
)) {
1558 snprintf(errstr
, errstr_len
,
1559 "Bad magic in inflight data: %08x",
1564 dumpcommand("Processing reply to command",
1566 command
= ntohl(prc
->req
.type
);
1567 from
= ntohll(prc
->req
.from
);
1568 len
= ntohl(prc
->req
.len
);
1570 switch (command
& NBD_CMD_MASK_COMMAND
) {
1573 uint64_t blknum
= from
>> 9;
1576 snprintf(errstr
, errstr_len
,
1577 "offset %llx beyond size %llx",
1578 (long long int)from
,
1579 (long long int)size
);
1582 READ_ALL_ERRCHK(sock
,
1586 "Could not read data: %s",
1588 if (--(blkhash
[blknum
].inflightr
) < 0) {
1589 snprintf(errstr
, errstr_len
,
1590 "Received a read reply for offset %llx when not in flight",
1591 (long long int)from
);
1594 /* work out what we was written */
1596 (dbuf
, blkhash
[blknum
].seq
,
1598 snprintf(errstr
, errstr_len
,
1599 "Bad reply data: I wanted blk %08x, seq %08x but I got (at a guess) blk %08x, seq %08x",
1600 (unsigned int)blknum
,
1601 blkhash
[blknum
].seq
,
1615 /* subsequent reads should get data with this seq */
1617 uint64_t blknum
= from
>> 9;
1618 if (--(blkhash
[blknum
].inflightw
) < 0) {
1619 snprintf(errstr
, errstr_len
,
1620 "Received a write reply for offset %llx when not in flight",
1621 (long long int)from
);
1624 blkhash
[blknum
].seq
=
1625 (uint32_t) (prc
->seq
);
1635 rclist_unlink(&inflight
, prc
);
1636 prc
->req
.magic
= 0; /* so a duplicate reply is detected */
1640 if ((do_print
== NULL
&& !(printer
++ % 5000))
1641 || !(readtransactionfile
|| txqueue
.numitems
1642 || inflight
.numitems
))
1644 ("%d: Seq %08lld Queued: %08d Inflight: %08d Done: %08lld\r",
1645 (int)mypid
, (long long int)seq
, txqueue
.numitems
,
1646 inflight
.numitems
, (long long int)processed
);
1652 if (gettimeofday(&stop
, NULL
) < 0) {
1653 snprintf(errstr
, errstr_len
, "Could not measure end time: %s",
1657 timespan
= timeval_diff_to_double(&stop
, &start
);
1658 speed
= xfer
/ timespan
;
1660 speed
= speed
/ 1024.0;
1664 speed
= speed
/ 1024.0;
1668 speed
= speed
/ 1024.0;
1672 ("%d: Integrity %s test complete. Took %.3f seconds to complete, %.3f%sib/s",
1673 (int)getpid(), (testflags
& TEST_WRITE
) ? "write" : "read",
1674 timespan
, speed
, speedchar
);
1680 close_connection(sock
, CONNECTION_CLOSE_PROPERLY
);
1683 if (size
&& blkhash
)
1684 munmap(blkhash
, (size
>> 9) * sizeof(struct blkitem
));
1686 if (blkhashfd
!= -1)
1696 g_warning("%s", errstr
);
1698 g_hash_table_destroy(handlehash
);
1703 void handle_nonopt(char *opt
, gchar
** hostname
, long int *p
)
1705 static int nonopt
= 0;
1709 *hostname
= g_strdup(opt
);
1713 *p
= (strtol(opt
, NULL
, 0));
1714 if (*p
== LONG_MIN
|| *p
== LONG_MAX
) {
1715 g_critical("Could not parse port number: %s",
1723 typedef int (*testfunc
) (gchar
*, gchar
*, int, char *, int, char, char, int);
1725 int main(int argc
, char **argv
)
1727 gchar
*hostname
= NULL
, *unixsock
= NULL
;
1733 testfunc test
= throughput_test
;
1739 /* Ignore SIGPIPE as we want to pick up the error from write() */
1740 signal(SIGPIPE
, SIG_IGN
);
1742 errstr
[errstr_len
] = '\0';
1745 g_message("%d: Not enough arguments", (int)getpid());
1746 g_message("%d: Usage: %s <hostname> <port>", (int)getpid(),
1748 g_message("%d: Or: %s <hostname> -N <exportname> [<port>]",
1749 (int)getpid(), argv
[0]);
1750 g_message("%d: Or: %s -u <unix socket> -N <exportname>",
1751 (int)getpid(), argv
[0]);
1755 while ((c
= getopt(argc
, argv
, "FN:t:owfilu:hC:K:A:H:")) >= 0) {
1758 handle_nonopt(optarg
, &hostname
, &p
);
1761 name
= g_strdup(optarg
);
1767 testflags
|= TEST_EXPECT_ERROR
;
1770 transactionlog
= g_strdup(optarg
);
1773 test
= oversize_test
;
1779 testflags
|= TEST_WRITE
;
1782 testflags
|= TEST_FLUSH
;
1785 test
= integrity_test
;
1788 unixsock
= g_strdup(optarg
);
1791 test
= handshake_test
;
1792 testflags
|= TEST_HANDSHAKE
;
1796 certfile
=g_strdup(optarg
);
1799 keyfile
=g_strdup(optarg
);
1802 cacertfile
=g_strdup(optarg
);
1805 tlshostname
=g_strdup(optarg
);
1812 g_warning("TLS support not compiled in");
1813 /* Do not change this - looked for by test suite */
1819 while (optind
< argc
) {
1820 handle_nonopt(argv
[optind
++], &hostname
, &p
);
1823 if (keyfile
&& !certfile
)
1824 certfile
= g_strdup(keyfile
);
1826 if (!tlshostname
&& hostname
)
1827 tlshostname
= g_strdup(hostname
);
1829 if (test(hostname
, unixsock
, (int)p
, name
, sock
, FALSE
, TRUE
, testflags
)
1831 g_warning("Could not run test: %s", errstr
);