Grrrrr different distributions ship different libsmbclient with different APIs
[amitay/dbench.git] / iscsi.c
1 /* 
2    Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2009
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #undef _GNU_SOURCE
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <stdint.h>
25 #include <arpa/inet.h>
26 #include "dbench.h"
27
28 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
29
30 #ifndef SG_DXFER_NONE
31 #define SG_DXFER_NONE -1
32 #endif
33 #ifndef SG_DXFER_TO_DEV
34 #define SG_DXFER_TO_DEV -2
35 #endif
36 #ifndef SG_DXFER_FROM_DEV
37 #define SG_DXFER_FROM_DEV -3
38 #endif
39
40 struct iscsi_device {
41        const char *portal;
42        const char *target;
43        int s;
44        uint64_t isid;
45        uint64_t blocks;
46        uint32_t itt;
47        uint32_t cmd_sn;
48        uint32_t exp_stat_sn;
49 };
50
51
52 void set_nonblocking(int fd)
53 {
54         unsigned v;
55         v = fcntl(fd, F_GETFL, 0);
56         fcntl(fd, F_SETFL, v | O_NONBLOCK);
57 }
58
59
60 struct login_param {
61        struct login_param *next;
62        char *arg;
63        char *value;
64 };
65 struct login_param *login_params;
66
67 static void add_login_param(char *arg, char *value)
68 {
69         struct login_param *new_param;
70
71         new_param = malloc(sizeof(struct login_param));
72         if (new_param == NULL) {
73                 printf("Failed to allocate login param struct\n");
74                 exit(10);
75         }
76         new_param->arg   = strdup(arg);
77         new_param->value = strdup(value);
78         new_param->next  = login_params;
79         login_params     = new_param;
80 }
81
82 static int send_iscsi_pdu(struct iscsi_device *sd, char *ish, char *data, int len)
83 {
84         char *buf, *ptr;
85         ssize_t remaining, count;
86
87         /* itt */
88         ish[16] = (sd->itt>>24)&0xff;
89         ish[17] = (sd->itt>>16)&0xff;
90         ish[18] = (sd->itt>> 8)&0xff;
91         ish[19] = (sd->itt    )&0xff;
92
93         /* command sequence number */
94         ish[24]  = (sd->cmd_sn>>24)&0xff;
95         ish[25]  = (sd->cmd_sn>>16)&0xff;
96         ish[26]  = (sd->cmd_sn>> 8)&0xff;
97         ish[27]  = (sd->cmd_sn    )&0xff;
98
99         /* expected stat sequence number */
100         ish[28]  = (sd->exp_stat_sn>>24)&0xff;
101         ish[29]  = (sd->exp_stat_sn>>16)&0xff;
102         ish[30]  = (sd->exp_stat_sn>> 8)&0xff;
103         ish[31]  = (sd->exp_stat_sn    )&0xff;
104
105         buf=malloc(48+len+4);
106         if (buf == NULL) {
107                 printf("Failed to allocate buffer for PDU of size %d bytes\n", 48+len);
108                 return -1;
109         }
110
111         memcpy(buf, ish, 48);
112         if (len > 0) {
113                 memcpy(buf+48, data, len);
114         }
115         remaining = 48 + len;
116         remaining = (remaining+3)&0xfffffc;
117         ptr = buf;
118         while(remaining > 0) {
119                 count = write(sd->s, ptr, remaining);
120                 if (count == -1) {
121                         printf("Write to socket failed with errno %d(%s)\n", errno, strerror(errno));
122                         free(buf);
123                         return -1;
124                 }
125                 remaining-= count;
126         }
127
128         free(buf);
129         return 0;
130 }
131
132 static int wait_for_pdu(struct iscsi_device *sd, char *ish, char *data, unsigned int *data_size)
133 {
134         char *buf, *ptr;
135         ssize_t total, remaining, count;
136         unsigned int itt, dsl;
137         uint32_t ecsn, ssn;
138
139         remaining = 48;
140         ptr = ish;
141         while (remaining > 0) {
142                 count = read(sd->s, ptr, remaining);
143                 if (count == -1) {
144                         printf("Read from socket failed with errno %d(%s)\n", errno, strerror(errno));
145                         return -1;
146                 }
147                 remaining-= count;
148                 ptr += count;
149         }
150
151         /* verify the itt */
152         itt  = (ish[16]&0xff)<<24;
153         itt |= (ish[17]&0xff)<<16;
154         itt |= (ish[18]&0xff)<< 8;
155         itt |= (ish[19]&0xff);
156         if (itt != sd->itt) {
157                 printf("Wrong ITT in PDU. Expected 0x%08x, got 0x%08x\n", sd->itt, itt);
158                 exit(10);
159         } 
160
161         /* data segment length */
162         dsl  = (ish[5]&0xff)<<16;
163         dsl |= (ish[6]&0xff)<<8;
164         dsl |= (ish[7]&0xff);
165
166         total = (dsl+3)&0xfffffffc;
167         remaining = total;
168         buf = malloc(remaining);
169         if (buf == NULL) {
170                 printf("Failed to alloc buf to read data into\n");
171                 return -1;
172         }
173         ptr = buf;
174         while (remaining > 0) {
175                 count = read(sd->s, ptr, remaining);
176
177                 if (count == -1) {
178                         printf("Read from socket failed with errno %d(%s)\n", errno, strerror(errno));
179                         return -1;
180                 }
181                 remaining-= count;
182                 ptr += count;
183         }
184
185         /* stat sequence number */
186         ssn  = (ish[24]&0xff)<<24;
187         ssn |= (ish[25]&0xff)<<16;
188         ssn |= (ish[26]&0xff)<<8;
189         ssn |= (ish[27]&0xff);
190         sd->exp_stat_sn = ssn+1;
191
192         /* expected command sequence number */
193         ecsn  = (ish[28]&0xff)<<24;
194         ecsn |= (ish[29]&0xff)<<16;
195         ecsn |= (ish[30]&0xff)<<8;
196         ecsn |= (ish[31]&0xff);
197         sd->cmd_sn = ecsn;
198
199         if (ish[0]&0x3f) {
200                 unsigned long int buffer_offset;
201
202                 buffer_offset  = (ish[40]&0xff)<<24;
203                 buffer_offset |= (ish[41]&0xff)<<16;
204                 buffer_offset |= (ish[42]&0xff)<<8;
205                 buffer_offset |= (ish[43]&0xff);
206
207                 if (buffer_offset == 0) {
208                         /* we only return the data from the first data-in pdu */
209                         if (data_size && *data_size > 0) {
210                                 if ((ssize_t)*data_size > total) {
211                                         *data_size = total;
212                                 }
213                                 if (data) {
214                                         memcpy(data, buf, *data_size);
215                                 }
216                         }
217                 }
218         }
219
220         free(buf);
221         return 0;
222 }
223
224 static int iscsi_login(struct child_struct *child, struct iscsi_device *sd)
225 {
226         char name[256];
227         char alias[256];
228         char ish[48];
229         int len;
230         struct login_param *login_param;
231         char *data, *ptr;
232
233         add_login_param("SessionType", "Normal");
234         add_login_param("HeaderDigest", "None");
235         add_login_param("DataDigest", "None");
236         add_login_param("DefaultTime2Wait", "0");
237         add_login_param("DefaultTime2Retain", "0");
238         add_login_param("InitialR2T", "Yes");
239         add_login_param("ImmediateData", "Yes");
240         add_login_param("MaxBurstLength", "16776192");
241         add_login_param("FirstBurstLength", "16776192");
242         add_login_param("MaxOutstandingR2T", "1");
243         add_login_param("MaxRecvDataSegmentLength", "16776192");
244         add_login_param("DataPDUInOrder", "Yes");
245         add_login_param("MaxConnections", "1");
246         add_login_param("TargetName", discard_const(sd->target));
247         sprintf(alias, "dbench:%d", child->id);
248         add_login_param("InitiatorAlias", alias);
249         sprintf(name, "iqn.2009-09.dbench:%d", child->id);
250         add_login_param("InitiatorName", name);
251
252
253         bzero(ish, 48);
254         /* opcode : LOGIN REQUEST (I) */
255         ish[0] = 0x43;
256
257         /* T CSG:op NSG:full feature */
258         ish[1] = 0x87;
259
260         /* data segment length */
261         for(login_param=login_params, len=0; login_param; login_param=login_param->next) {
262                 len += strlen(login_param->arg);
263                 len += 1;
264                 len += strlen(login_param->value);
265                 len += 1;
266         }
267         /* data segment length */
268         ish[5] = (len>>16)&0xff;
269         ish[6] = (len>> 8)&0xff;
270         ish[7] = (len    )&0xff;
271
272
273         /* isid */
274         ish[8] = (sd->isid>>40)&0xff;
275         ish[9] = (sd->isid>>32)&0xff;
276         ish[10] = (sd->isid>>24)&0xff;
277         ish[11] = (sd->isid>>16)&0xff;
278         ish[12] = (sd->isid>> 8)&0xff;
279         ish[13] = (sd->isid    )&0xff;
280         
281         data = malloc(len);
282         for(login_param=login_params, ptr=data; login_param; login_param=login_param->next) {
283                 strcpy(ptr,login_param->arg);
284                 ptr+=strlen(login_param->arg);
285                 *ptr='=';
286                 ptr++;
287                 strcpy(ptr,login_param->value);
288                 ptr+=strlen(login_param->value);
289                 *ptr=0;
290                 ptr++;
291         }
292
293         if (send_iscsi_pdu(sd, ish, data, len) != 0) {
294                 printf("Failed to send iscsi pdu\n");
295                 return -1;
296         }
297
298         if (wait_for_pdu(sd, ish, NULL, NULL) != 0) {
299                 printf("Failed to send iscsi pdu\n");
300                 return -1;
301         }
302
303
304
305         free(data);
306         return 0;
307 }
308
309
310
311
312
313
314
315
316
317
318
319 /* XXX merge with scsi.c */
320 static int check_sense(unsigned char sc, const char *expected)
321 {
322         if (strcmp(expected, "*") == 0){
323                 return 1;
324         }
325         if (strncmp(expected, "0x", 2) == 0) {
326                 return sc == strtol(expected, NULL, 16);
327         }
328         return 0;
329 }
330 static void failed(struct child_struct *child)
331 {
332         child->failed = 1;
333         printf("ERROR: child %d failed at line %d\n", child->id, child->line);
334         exit(1);
335 }
336
337
338
339 static int do_iscsi_io(struct iscsi_device *sd, unsigned char *cdb, unsigned char cdb_size, int xfer_dir, unsigned int *data_size, char *data, unsigned char *sc)
340 {
341         char ish[48];
342         int data_in_len=0, data_out_len=0;
343
344         bzero(ish, 48);
345
346         /* opcode : SCSI command */
347         ish[0] = 0x01;
348
349         /* flags */
350         ish[1] = 0x81; /* F + SIMPLE */
351         if (xfer_dir == SG_DXFER_FROM_DEV) {
352                 ish[1] |= 0x40;
353                 data_in_len= *data_size;
354                 data_out_len=0;
355         }
356         if (xfer_dir == SG_DXFER_TO_DEV) {
357                 ish[1] |= 0x20;
358
359                 /* data segment length */
360                 ish[5] = ((*data_size)>>16)&0xff;
361                 ish[6] = ((*data_size)>> 8)&0xff;
362                 ish[7] = ((*data_size)    )&0xff;
363
364                 data_in_len=0;
365                 data_out_len=*data_size;
366         }
367
368         /* lun */
369         ish[9] = options.iscsi_lun;
370
371         /* expected data xfer len */
372         ish[20]  = ((*data_size)>>24)&0xff;
373         ish[21]  = ((*data_size)>>16)&0xff;
374         ish[22]  = ((*data_size)>> 8)&0xff;
375         ish[23]  = ((*data_size)    )&0xff;
376
377         /* cdb */
378         memcpy(ish+32, cdb, cdb_size);
379
380         *data_size=data_out_len;
381         if (send_iscsi_pdu(sd, ish, data, *data_size) != 0) {
382                 printf("Failed to send iscsi pdu\n");
383                 return -1;
384         }
385
386 need_more_data:
387         *data_size=data_in_len;
388         if (wait_for_pdu(sd, ish, data, data_size) != 0) {
389                 printf("Failed to receive iscsi pdu\n");
390                 return -1;
391         }
392
393         switch (ish[0]&0x3f) {
394         case 0x21: /* SCSI response */
395                 if (ish[2] != 0) {
396                         printf("SCSI Response %d\n", ish[2]);
397                         sd->itt++;
398                         return -1;
399                 }
400                 if (ish[3] == 0) {
401                         *sc = 0;
402                         sd->itt++;
403                         return 0;
404                 }
405                 if (ish[3] == 2) {
406                         *sc = 2;
407                         sd->itt++;
408                         return 0;
409                 }
410                 break;
411         case 0x25: /* SCSI Data-In */
412                 if (ish[1]&0x01) {
413                         *sc = ish[3];
414                         sd->itt++;
415                         return 0;
416                 }
417                 /* no sbit, it means there is more data to read */
418                 goto need_more_data;
419                 break;
420         default:
421                 printf("got unsupported PDU:0x%02x\n", ish[0]&0x3f);
422         }
423
424         *sc = 0;
425         sd->itt++;
426         return 0;
427 }
428
429 static void iscsi_testunitready(struct dbench_op *op)
430 {
431         struct iscsi_device *sd;
432         unsigned char cdb[]={0,0,0,0,0,0};
433         int res;
434         unsigned char sc;
435         unsigned int data_size=0;
436
437         sd = op->child->private;
438
439         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_NONE, &data_size, NULL, &sc);
440         if(res){
441                 printf("SCSI_IO failed\n");
442                 failed(op->child);
443         }
444         if (!check_sense(sc, op->status)) {
445                 printf("[%d] TESTUNITREADY \"%s\" failed (0x%02x) - expected %s\n", 
446                        op->child->line, op->fname, sc, op->status);
447                 failed(op->child);
448         }
449         return;
450 }
451
452 static void iscsi_read10(struct dbench_op *op)
453 {
454         struct iscsi_device *sd=op->child->private;
455         unsigned char cdb[]={0x28,0,0,0,0,0,0,0,0,0};
456         int res;
457         uint32_t lba = op->params[0];
458         uint32_t xferlen = op->params[1];
459         int rd = op->params[2];
460         int grp = op->params[3];
461         unsigned int data_size=1024*1024;
462         char data[data_size];
463         unsigned char sc;
464
465         lba = (lba / xferlen) * xferlen;
466
467         /* make sure we wrap properly instead of failing if the loadfile
468            is bigger than our device
469         */
470         if (sd->blocks <= lba) {
471                 lba = lba%sd->blocks;
472         }
473         if (sd->blocks <= lba+xferlen) {
474                 xferlen=1;
475         }
476
477         cdb[1] = rd;
478
479         cdb[2] = (lba>>24)&0xff;
480         cdb[3] = (lba>>16)&0xff;
481         cdb[4] = (lba>> 8)&0xff;
482         cdb[5] = (lba    )&0xff;
483
484         cdb[6] = grp&0x1f;
485
486         cdb[7] = (xferlen>>8)&0xff;
487         cdb[8] = xferlen&0xff;
488         data_size = xferlen*512;
489
490         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
491         if(res){
492                 printf("SCSI_IO failed\n");
493                 failed(op->child);
494         }
495         if (!check_sense(sc, op->status)) {
496                 printf("[%d] READ10 \"%s\" failed (0x%02x) - expected %s\n", 
497                        op->child->line, op->fname, sc, op->status);
498                 failed(op->child);
499         }
500
501         op->child->bytes += xferlen*512;
502 }
503
504
505
506 static void local_iscsi_readcapacity10(struct dbench_op *op, uint64_t *blocks)
507 {
508         struct iscsi_device *sd;
509         unsigned char cdb[]={0x25,0,0,0,0,0,0,0,0,0};
510         int res;
511         int lba = op->params[0];
512         int pmi = op->params[1];
513         unsigned int data_size=8;
514         char data[data_size];
515         unsigned char sc;
516
517         cdb[2] = (lba>>24)&0xff;
518         cdb[3] = (lba>>16)&0xff;
519         cdb[4] = (lba>> 8)&0xff;
520         cdb[5] = (lba    )&0xff;
521
522         cdb[8] = (pmi?1:0);
523
524         sd = op->child->private;
525
526         res=do_iscsi_io(sd, cdb, sizeof(cdb), SG_DXFER_FROM_DEV, &data_size, data, &sc);
527         if(res){
528                 printf("SCSI_IO failed\n");
529                 failed(op->child);
530         }
531         if (!check_sense(sc, op->status)) {
532                 printf("[%d] READCAPACITY10 \"%s\" failed (0x%02x) - expected %s\n", 
533                        op->child->line, op->fname, sc, op->status);
534                 failed(op->child);
535         }
536
537         if (blocks) {
538                 *blocks  = (data[0]&0xff)<<24;
539                 *blocks |= (data[1]&0xff)<<16;
540                 *blocks |= (data[2]&0xff)<<8;
541                 *blocks |= (data[3]&0xff);
542         }
543 }
544
545 static void iscsi_readcapacity10(struct dbench_op *op)
546 {
547         return local_iscsi_readcapacity10(op, NULL);
548 }
549
550 static void iscsi_setup(struct child_struct *child)
551 {
552         struct iscsi_device *sd;
553         struct sockaddr_in sin;
554         struct dbench_op fake_op;
555
556         sd = malloc(sizeof(struct iscsi_device));
557         if (sd == NULL) {
558                 printf("Failed to allocate iscsi device structure\n");
559                 exit(10);
560         }
561         child->private=sd;
562
563         sd->portal=options.iscsi_portal;
564         sd->target=options.iscsi_target;
565         sd->isid  =0x0000800000000000ULL | child->id; 
566         sd->s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
567         if (sd->s == -1) {
568                 printf("could not open socket() errno:%d(%s)\n", errno, strerror(errno));
569                 exit(10);
570         }
571
572         sin.sin_family      = AF_INET;
573         sin.sin_port        = htons(options.iscsi_port);
574         if (inet_pton(AF_INET, sd->portal, &sin.sin_addr) != 1) {
575                 printf("Failed to convert \"%s\" into an address\n", sd->portal);
576                 exit(10);
577         }
578
579         if (connect(sd->s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
580                 printf("connect failed with errno:%d(%s)\n", errno, strerror(errno));
581                 exit(10);
582         }
583
584         sd->itt=0x000a0000;
585         sd->cmd_sn=0;
586         sd->exp_stat_sn=0;
587         if (iscsi_login(child, sd) != 0) {
588                 printf("Failed to log in to target.\n");
589                 exit(10);
590         }
591
592         fake_op.child=child;
593         fake_op.status="*";
594         iscsi_testunitready(&fake_op);
595
596         fake_op.params[0]=0;
597         fake_op.params[1]=0;
598         fake_op.status="*";
599         local_iscsi_readcapacity10(&fake_op, &sd->blocks);
600 }
601
602        
603 static void iscsi_cleanup(struct child_struct *child)
604 {
605         struct iscsi_device *sd;
606
607         sd=child->private;
608         close(sd->s);
609         sd->s=-1;
610         free(sd);
611 }
612
613 static int iscsi_init(void)
614 {
615         struct iscsi_device *sd;
616         struct sockaddr_in sin;
617         struct dbench_op fake_op;
618         struct child_struct child;
619
620         sd = malloc(sizeof(struct iscsi_device));
621         if (sd == NULL) {
622                 printf("Failed to allocate iscsi device structure\n");
623                 return 1;
624         }
625         child.private=sd;
626         child.id=99999;
627
628         sd->portal=options.iscsi_portal;
629         sd->target=options.iscsi_target;
630         sd->isid  =0x0000800000000000ULL | child.id; 
631         sd->s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
632         if (sd->s == -1) {
633                 printf("could not open socket() errno:%d(%s)\n", errno, strerror(errno));
634                 return 1;
635         }
636
637         sin.sin_family      = AF_INET;
638         sin.sin_port        = htons(options.iscsi_port);
639         if (inet_pton(AF_INET, sd->portal, &sin.sin_addr) != 1) {
640                 printf("Failed to convert \"%s\" into an address\n", sd->portal);
641                 return 1;
642         }
643
644         if (connect(sd->s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
645                 printf("connect failed with errno:%d(%s)\n", errno, strerror(errno));
646                 return 1;
647         }
648
649         sd->itt=0x000a0000;
650         sd->cmd_sn=0;
651         sd->exp_stat_sn=0;
652         if (iscsi_login(&child, sd) != 0) {
653                 printf("Failed to log in to target.\n");
654                 return 1;
655         }
656
657         fake_op.child=&child;
658         fake_op.status="*";
659         iscsi_testunitready(&fake_op);
660
661         fake_op.params[0]=0;
662         fake_op.params[1]=0;
663         fake_op.status="*";
664         local_iscsi_readcapacity10(&fake_op, &sd->blocks);
665
666         close(sd->s);
667         free(sd);
668
669         return 0;
670 }
671
672
673 static struct backend_op ops[] = {
674         { "TESTUNITREADY",    iscsi_testunitready },
675         { "READ10",           iscsi_read10 },
676         { "READCAPACITY10",   iscsi_readcapacity10 },
677         { NULL, NULL}
678 };
679
680 struct nb_operations iscsi_ops = {
681         .backend_name = "iscsibench",
682         .init         = iscsi_init,
683         .setup        = iscsi_setup,
684         .cleanup      = iscsi_cleanup,
685         .ops          = ops
686 };
687
688