00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <stdbool.h>
00027 #include <string.h>
00028 #include <sys/time.h>
00029 #include <sys/types.h>
00030 #include <sys/socket.h>
00031 #include <syslog.h>
00032 #include <unistd.h>
00033 #include "config.h"
00034 #include "lfs.h"
00035 #define MY_NAME "nbd-tester-client"
00036 #include "cliserv.h"
00037
00038 #include <netinet/in.h>
00039 #include <glib.h>
00040
00041 static gchar errstr[1024];
00042 const static int errstr_len=1024;
00043
00044 static uint64_t size;
00045
00046 typedef enum {
00047 CONNECTION_TYPE_NONE,
00048 CONNECTION_TYPE_CONNECT,
00049 CONNECTION_TYPE_INIT_PASSWD,
00050 CONNECTION_TYPE_CLISERV,
00051 CONNECTION_TYPE_FULL,
00052 } CONNECTION_TYPE;
00053
00054 typedef enum {
00055 CONNECTION_CLOSE_PROPERLY,
00056 CONNECTION_CLOSE_FAST,
00057 } CLOSE_TYPE;
00058
00059 inline int read_all(int f, void *buf, size_t len) {
00060 ssize_t res;
00061 size_t retval=0;
00062
00063 while(len>0) {
00064 if((res=read(f, buf, len)) <=0) {
00065 snprintf(errstr, errstr_len, "Read failed: %s", strerror(errno));
00066 return -1;
00067 }
00068 len-=res;
00069 buf+=res;
00070 retval+=res;
00071 }
00072 return retval;
00073 }
00074
00075 int setup_connection(gchar *hostname, int port, gchar* name, CONNECTION_TYPE ctype) {
00076 int sock;
00077 struct hostent *host;
00078 struct sockaddr_in addr;
00079 char buf[256];
00080 uint64_t mymagic = (name ? opts_magic : cliserv_magic);
00081 u64 tmp64;
00082 uint32_t tmp32 = 0;
00083
00084 sock=0;
00085 if(ctype<CONNECTION_TYPE_CONNECT)
00086 goto end;
00087 if((sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
00088 strncpy(errstr, strerror(errno), errstr_len);
00089 goto err;
00090 }
00091 setmysockopt(sock);
00092 if(!(host=gethostbyname(hostname))) {
00093 strncpy(errstr, strerror(errno), errstr_len);
00094 goto err_open;
00095 }
00096 addr.sin_family=AF_INET;
00097 addr.sin_port=htons(port);
00098 addr.sin_addr.s_addr=*((int *) host->h_addr);
00099 if((connect(sock, (struct sockaddr *)&addr, sizeof(addr))<0)) {
00100 strncpy(errstr, strerror(errno), errstr_len);
00101 goto err_open;
00102 }
00103 if(ctype<CONNECTION_TYPE_INIT_PASSWD)
00104 goto end;
00105 if(read_all(sock, buf, strlen(INIT_PASSWD))<0) {
00106 snprintf(errstr, errstr_len, "Could not read INIT_PASSWD: %s",
00107 strerror(errno));
00108 goto err_open;
00109 }
00110 if(strlen(buf)==0) {
00111 snprintf(errstr, errstr_len, "Server closed connection");
00112 goto err_open;
00113 }
00114 if(strncmp(buf, INIT_PASSWD, strlen(INIT_PASSWD))) {
00115 snprintf(errstr, errstr_len, "INIT_PASSWD does not match");
00116 goto err_open;
00117 }
00118 if(ctype<CONNECTION_TYPE_CLISERV)
00119 goto end;
00120 if(read_all(sock, &tmp64, sizeof(tmp64))<0) {
00121 snprintf(errstr, errstr_len, "Could not read cliserv_magic: %s",
00122 strerror(errno));
00123 goto err_open;
00124 }
00125 tmp64=ntohll(tmp64);
00126 if(tmp64 != mymagic) {
00127 strncpy(errstr, "mymagic does not match", errstr_len);
00128 goto err_open;
00129 }
00130 if(ctype<CONNECTION_TYPE_FULL)
00131 goto end;
00132 if(!name) {
00133 read_all(sock, &size, sizeof(size));
00134 size=ntohll(size);
00135 read_all(sock, buf, 128);
00136 goto end;
00137 }
00138
00139 read_all(sock, buf, sizeof(uint16_t));
00140
00141 write(sock, &tmp32, sizeof(tmp32));
00142
00143 tmp64 = htonll(opts_magic);
00144 write(sock, &tmp64, sizeof(tmp64));
00145
00146 tmp32 = htonl(NBD_OPT_EXPORT_NAME);
00147 write(sock, &tmp32, sizeof(tmp32));
00148 tmp32 = htonl((uint32_t)strlen(name));
00149 write(sock, &tmp32, sizeof(tmp32));
00150 write(sock, name, strlen(name));
00151 read_all(sock, &size, sizeof(size));
00152 size = ntohll(size);
00153 read_all(sock, buf, sizeof(uint16_t)+124);
00154 goto end;
00155 err_open:
00156 close(sock);
00157 err:
00158 sock=-1;
00159 end:
00160 return sock;
00161 }
00162
00163 int close_connection(int sock, CLOSE_TYPE type) {
00164 struct nbd_request req;
00165 u64 counter=0;
00166
00167 switch(type) {
00168 case CONNECTION_CLOSE_PROPERLY:
00169 req.magic=htonl(NBD_REQUEST_MAGIC);
00170 req.type=htonl(NBD_CMD_DISC);
00171 memcpy(&(req.handle), &(counter), sizeof(counter));
00172 counter++;
00173 req.from=0;
00174 req.len=0;
00175 if(write(sock, &req, sizeof(req))<0) {
00176 snprintf(errstr, errstr_len, "Could not write to socket: %s", strerror(errno));
00177 return -1;
00178 }
00179 case CONNECTION_CLOSE_FAST:
00180 if(close(sock)<0) {
00181 snprintf(errstr, errstr_len, "Could not close socket: %s", strerror(errno));
00182 return -1;
00183 }
00184 break;
00185 default:
00186 g_critical("Your compiler is on crack!");
00187 return -1;
00188 }
00189 return 0;
00190 }
00191
00192 int read_packet_check_header(int sock, size_t datasize, long long int curhandle) {
00193 struct nbd_reply rep;
00194 int retval=0;
00195 char buf[datasize];
00196
00197 read_all(sock, &rep, sizeof(rep));
00198 rep.magic=ntohl(rep.magic);
00199 rep.error=ntohl(rep.error);
00200 if(rep.magic!=NBD_REPLY_MAGIC) {
00201 snprintf(errstr, errstr_len, "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", curhandle, curhandle, *((u64*)rep.handle), *((u64*)rep.handle), (long unsigned int)rep.magic, (long unsigned int)NBD_REPLY_MAGIC);
00202 retval=-1;
00203 goto end;
00204 }
00205 if(rep.error) {
00206 snprintf(errstr, errstr_len, "Received error from server: %ld (0x%lX). Handle is %lld (0x%llX).", (long int)rep.error, (long unsigned int)rep.error, (long long int)(*((u64*)rep.handle)), *((u64*)rep.handle));
00207 retval=-1;
00208 goto end;
00209 }
00210 read_all(sock, &buf, datasize);
00211
00212 end:
00213 return retval;
00214 }
00215
00216 int oversize_test(gchar* hostname, int port, char* name, int sock, char sock_is_open, char close_sock) {
00217 int retval=0;
00218 struct nbd_request req;
00219 struct nbd_reply rep;
00220 int request=0;
00221 int i=0;
00222 pid_t mypid = getpid();
00223 char buf[((1024*1024)+sizeof(struct nbd_request)/2)<<1];
00224 bool got_err;
00225
00226
00227 if(!sock_is_open) {
00228 if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL))<0) {
00229 g_warning("Could not open socket: %s", errstr);
00230 retval=-1;
00231 goto err;
00232 }
00233 }
00234 req.magic=htonl(NBD_REQUEST_MAGIC);
00235 req.type=htonl(NBD_CMD_READ);
00236 req.len=htonl(1024*1024);
00237 memcpy(&(req.handle),&i,sizeof(i));
00238 req.from=htonll(i);
00239 write(sock, &req, sizeof(req));
00240 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00241 read_all(sock, &rep, sizeof(struct nbd_reply));
00242 read_all(sock, &buf, ntohl(req.len));
00243 if(rep.error) {
00244 printf("Received unexpected error\n");
00245 retval=-1;
00246 goto err;
00247 } else {
00248 printf("OK\n");
00249 }
00250
00251 i++; req.from=htonll(i);
00252 req.len = htonl(ntohl(req.len) + sizeof(struct nbd_request) / 2);
00253 write(sock, &req, sizeof(req));
00254 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00255 read_all(sock, &rep, sizeof(struct nbd_reply));
00256 read_all(sock, &buf, ntohl(req.len));
00257 if(rep.error) {
00258 printf("Received expected error\n");
00259 got_err=true;
00260 } else {
00261 printf("OK\n");
00262 got_err=false;
00263 }
00264
00265 i++; req.from=htonll(i);
00266 req.len = htonl(ntohl(req.len) << 1);
00267 write(sock, &req, sizeof(req));
00268 printf("%d: testing oversized request: %d: ", getpid(), ntohl(req.len));
00269 read_all(sock, &rep, sizeof(struct nbd_reply));
00270 read_all(sock, &buf, ntohl(req.len));
00271 if(rep.error) {
00272 printf("error\n");
00273 } else {
00274 printf("OK\n");
00275 }
00276 if((rep.error && !got_err) || (!rep.error && got_err)) {
00277 printf("Received unexpected error\n");
00278 retval=-1;
00279 }
00280 err:
00281 return retval;
00282 }
00283
00284 int throughput_test(gchar* hostname, int port, char* name, int sock, char sock_is_open, char close_sock) {
00285 long long int i;
00286 char buf[1024];
00287 struct nbd_request req;
00288 int requests=0;
00289 fd_set set;
00290 struct timeval tv;
00291 struct timeval start;
00292 struct timeval stop;
00293 float timespan;
00294 int speed;
00295 char speedchar[2] = { '\0', '\0' };
00296 int retval=0;
00297 size_t tmp;
00298 signed int do_write=TRUE;
00299 pid_t mypid = getpid();
00300
00301 size=0;
00302 if(!sock_is_open) {
00303 if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL))<0) {
00304 g_warning("Could not open socket: %s", errstr);
00305 retval=-1;
00306 goto err;
00307 }
00308 }
00309 req.magic=htonl(NBD_REQUEST_MAGIC);
00310 req.type=htonl(NBD_CMD_READ);
00311 req.len=htonl(1024);
00312 if(gettimeofday(&start, NULL)<0) {
00313 retval=-1;
00314 snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
00315 goto err_open;
00316 }
00317 for(i=0;i+1024<=size;i+=1024) {
00318 if(do_write) {
00319 memcpy(&(req.handle),&i,sizeof(i));
00320 req.from=htonll(i);
00321 write(sock, &req, sizeof(req));
00322 printf("%d: Requests(+): %d\n", (int)mypid, ++requests);
00323 }
00324 do {
00325 FD_ZERO(&set);
00326 FD_SET(sock, &set);
00327 tv.tv_sec=0;
00328 tv.tv_usec=0;
00329 select(sock+1, &set, NULL, NULL, &tv);
00330 if(FD_ISSET(sock, &set)) {
00331
00332
00333 if(read_packet_check_header(sock, 1024, i)<0) {
00334 retval=-1;
00335 goto err_open;
00336 }
00337 printf("%d: Requests(-): %d\n", (int)mypid, --requests);
00338 }
00339 } while FD_ISSET(sock, &set);
00340
00341
00342 FD_ZERO(&set);
00343 FD_SET(sock, &set);
00344 tv.tv_sec=1;
00345 tv.tv_usec=0;
00346 do_write=select(sock+1,NULL,&set,NULL,&tv);
00347 if(!do_write) printf("Select finished\n");
00348 if(do_write<0) {
00349 snprintf(errstr, errstr_len, "select: %s", strerror(errno));
00350 retval=-1;
00351 goto err_open;
00352 }
00353 }
00354
00355 do {
00356 FD_ZERO(&set);
00357 FD_SET(sock, &set);
00358 tv.tv_sec=0;
00359 tv.tv_usec=0;
00360 select(sock+1, &set, NULL, NULL, &tv);
00361 if(FD_ISSET(sock, &set)) {
00362
00363
00364 read_packet_check_header(sock, 1024, i);
00365 printf("%d: Requests(-): %d\n", (int)mypid, --requests);
00366 }
00367 } while (requests);
00368 if(gettimeofday(&stop, NULL)<0) {
00369 retval=-1;
00370 snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
00371 goto err_open;
00372 }
00373 timespan=(float)(stop.tv_sec-start.tv_sec+(stop.tv_usec-start.tv_usec))/(float)1000000;
00374 speed=(int)(size/timespan);
00375 if(speed>1024) {
00376 speed>>=10;
00377 speedchar[0]='K';
00378 }
00379 if(speed>1024) {
00380 speed>>=10;
00381 speedchar[0]='M';
00382 }
00383 if(speed>1024) {
00384 speed>>=10;
00385 speedchar[0]='G';
00386 }
00387 g_message("%d: Throughput test complete. Took %.3f seconds to complete, %d%siB/s", (int)getpid(), timespan,speed,speedchar);
00388
00389 err_open:
00390 if(close_sock) {
00391 close_connection(sock, CONNECTION_CLOSE_PROPERLY);
00392 }
00393 err:
00394 return retval;
00395 }
00396
00397 typedef int (*testfunc)(gchar*, int, char*, int, char, char);
00398
00399 int main(int argc, char**argv) {
00400 gchar *hostname;
00401 long int p = 0;
00402 char* name = NULL;
00403 int sock=0;
00404 char c;
00405 bool want_port = TRUE;
00406 int nonopt=0;
00407 testfunc test = throughput_test;
00408
00409 if(argc<3) {
00410 g_message("%d: Not enough arguments", (int)getpid());
00411 g_message("%d: Usage: %s <hostname> <port>", (int)getpid(), argv[0]);
00412 g_message("%d: Or: %s <hostname> -N <exportname>", (int)getpid(), argv[0]);
00413 exit(EXIT_FAILURE);
00414 }
00415 logging();
00416 while((c=getopt(argc, argv, "-N:o"))>=0) {
00417 switch(c) {
00418 case 1:
00419 switch(nonopt) {
00420 case 0:
00421 hostname=g_strdup(optarg);
00422 nonopt++;
00423 break;
00424 case 1:
00425 if(want_port)
00426 p=(strtol(argv[2], NULL, 0));
00427 if(p==LONG_MIN||p==LONG_MAX) {
00428 g_critical("Could not parse port number: %s", strerror(errno));
00429 exit(EXIT_FAILURE);
00430 }
00431 break;
00432 }
00433 break;
00434 case 'N':
00435 name=g_strdup(optarg);
00436 p = 10809;
00437 want_port = false;
00438 break;
00439 case 'o':
00440 test=oversize_test;
00441 break;
00442 }
00443 }
00444
00445 if(test(hostname, (int)p, name, sock, FALSE, TRUE)<0) {
00446 g_warning("Could not run test: %s", errstr);
00447 exit(EXIT_FAILURE);
00448 }
00449
00450 return 0;
00451 }