Network Block Device 2.9.20
|
00001 /* 00002 * Test client to test the NBD server. Doesn't do anything useful, except 00003 * checking that the server does, actually, work. 00004 * 00005 * Note that the only 'real' test is to check the client against a kernel. If 00006 * it works here but does not work in the kernel, then that's most likely a bug 00007 * in this program and/or in nbd-server. 00008 * 00009 * Copyright(c) 2006 Wouter Verhelst 00010 * 00011 * This program is Free Software; you can redistribute it and/or modify it 00012 * under the terms of the GNU General Public License as published by the Free 00013 * Software Foundation, in version 2. 00014 * 00015 * This program is distributed in the hope that it will be useful, but WITHOUT 00016 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00017 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 00018 * more details. 00019 * 00020 * You should have received a copy of the GNU General Public License along with 00021 * this program; if not, write to the Free Software Foundation, Inc., 51 00022 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 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 /* flags */ 00139 read_all(sock, buf, sizeof(uint16_t)); 00140 /* reserved field */ 00141 write(sock, &tmp32, sizeof(tmp32)); 00142 /* magic */ 00143 tmp64 = htonll(opts_magic); 00144 write(sock, &tmp64, sizeof(tmp64)); 00145 /* name */ 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!"); /* or I am buggy */ 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 /* This should work */ 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 /* This probably should not work */ 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 /* ... unless this works, too */ 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 /* Okay, there's something ready for 00332 * reading here */ 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 /* Now wait until we can write again or until a second have 00341 * passed, whichever comes first*/ 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 /* Now empty the read buffer */ 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 /* Okay, there's something ready for 00363 * reading here */ 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 }