Browse Source

iscsi: new module from Ronnie.

Rusty Russell 15 years ago
parent
commit
cb522f25ca

+ 39 - 0
ccan/iscsi/Makefile

@@ -0,0 +1,39 @@
+LIBS=
+CC=gcc
+CFLAGS=-g -O0 -Wall -W -I../.. "-D_U_=__attribute__((unused))"
+LIBISCSI_OBJ = socket.o init.o login.o nop.o pdu.o discovery.o scsi-command.o scsi-lowlevel.o
+
+all: tools/iscsiclient
+
+tools/iscsiclient: tools/iscsiclient.o libiscsi.a
+	$(CC) $(CFLAGS) -o $@ tools/iscsiclient.o libiscsi.a $(LIBS)
+
+libiscsi.a: $(LIBISCSI_OBJ)
+	@echo Creating library $@
+	ar r libiscsi.a $(LIBISCSI_OBJ) 
+	ranlib libiscsi.a
+
+tools/iscsiclient.o: tools/iscsiclient.c
+	@echo Compiling $@
+	$(CC) $(CFLAGS) -c tools/iscsiclient.c -o $@
+
+socket.o: socket.c iscsi.h iscsi-private.h
+
+init.o: init.c iscsi.h iscsi-private.h
+
+login.o: login.c iscsi.h iscsi-private.h
+
+pdu.o: pdu.c iscsi.h iscsi-private.h
+
+nop.o: nop.c iscsi.h iscsi-private.h
+
+discovery.o: discovery.c iscsi.h iscsi-private.h
+
+scsi-command.o: scsi-command.c iscsi.h iscsi-private.h
+
+scsi-lowlevel.o: scsi-lowlevel.c scsi-lowlevel.h
+
+clean:
+	rm -f tools/iscsiclient
+	rm -f *.o
+	rm -f libiscsi.a

+ 28 - 0
ccan/iscsi/_info

@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * iscsi - async library for iscsi functionality
+ *
+ * The iscsi module is a work in progress.
+ *
+ * It aims to become a full async library for iscsi functionality,
+ * including all features required to establish and maintain a iscsi
+ * session, as well as a low level scsi library to create scsi cdb's
+ * and parse/unmarshall data-in structures.
+ *
+ * License: GPL (3 or any later version)
+ * Author: Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ */
+int main(int argc, char *argv[])
+{
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		return 0;
+	}
+
+	return 1;
+}
+

+ 172 - 0
ccan/iscsi/discovery.c

@@ -0,0 +1,172 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	char *str;
+
+	if (iscsi == NULL) {
+		printf("trying to send text on NULL context\n");
+		return -1;
+	}
+
+	if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
+		printf("Trying to do discovery on non-discovery session\n");
+		return -2;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST, ISCSI_PDU_TEXT_RESPONSE);
+	if (pdu == NULL) {
+		printf("Failed to allocate text pdu\n");
+		return -3;
+	}
+
+	/* immediate */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
+
+	/* target transfer tag */
+	iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+	/* sendtargets */
+	str = "SendTargets=All";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		printf("failed to queue iscsi text pdu\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -5;
+	}
+
+	return 0;
+}
+
+static void iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
+{
+	while (addresses != NULL) {
+		struct iscsi_discovery_address *next = addresses->next;
+
+		if (addresses->target_name != NULL) {
+			free(discard_const(addresses->target_name));
+			addresses->target_name = NULL;
+		}
+		if (addresses->target_address != NULL) {
+			free(discard_const(addresses->target_address));
+			addresses->target_address = NULL;
+		}
+		addresses->next = NULL;
+		free(addresses);
+		addresses = next;
+	}
+}	
+
+int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+	struct iscsi_discovery_address *targets = NULL;
+
+	/* verify the response looks sane */
+	if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
+		printf("unsupported flags in text reply %02x\n", hdr[1]);
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+		return -1;
+	}
+
+	/* skip past the header */
+	hdr  += ISCSI_HEADER_SIZE;
+	size -= ISCSI_HEADER_SIZE;
+
+	while (size > 0) {
+		int len;
+
+		len = strlen((char *)hdr);
+
+		if (len == 0) {
+			break;
+		}
+
+		if (len > size) {
+			printf("len > size when parsing discovery data %d>%d\n", len, size);
+			pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+			iscsi_free_discovery_addresses(targets);
+			return -1;
+		} 
+
+		/* parse the strings */
+		if (!strncmp((char *)hdr, "TargetName=", 11)) {
+			struct iscsi_discovery_address *target;
+
+			target = malloc(sizeof(struct iscsi_discovery_address));
+			if (target == NULL) {
+				printf("Failed to allocate data for new discovered target\n");
+				pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+			bzero(target, sizeof(struct iscsi_discovery_address));
+			target->target_name = strdup((char *)hdr+11);
+			if (target->target_name == NULL) {
+				printf("Failed to allocate data for new discovered target name\n");
+				pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+				free(target);
+				target = NULL;
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+			target->next = targets;
+			targets = target;
+		} else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
+			targets->target_address = strdup((char *)hdr+14);
+			if (targets->target_address == NULL) {
+				printf("Failed to allocate data for new discovered target address\n");
+				pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+		} else {
+			printf("Dont know how to handle discovery string : %s\n", hdr);
+			pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+			iscsi_free_discovery_addresses(targets);
+			return -1;
+		}
+
+		hdr  += len + 1;
+		size -= len + 1;
+	}
+
+	pdu->callback(iscsi, ISCSI_STATUS_GOOD, targets, pdu->private_data);
+	iscsi_free_discovery_addresses(targets);
+
+	return 0;
+}

+ 181 - 0
ccan/iscsi/dlinklist.h

@@ -0,0 +1,181 @@
+/* 
+   Unix SMB/CIFS implementation.
+   some simple double linked list macros
+
+   Copyright (C) Andrew Tridgell 1998-2010
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* To use these macros you must have a structure containing a next and
+   prev pointer */
+
+#ifndef _DLINKLIST_H
+#define _DLINKLIST_H
+
+/*
+  February 2010 - changed list format to have a prev pointer from the
+  list head. This makes DLIST_ADD_END() O(1) even though we only have
+  one list pointer.
+
+  The scheme is as follows:
+
+     1) with no entries in the list:
+          list_head == NULL
+
+     2) with 1 entry in the list:
+          list_head->next == NULL
+          list_head->prev == list_head
+
+     3) with 2 entries in the list:
+          list_head->next == element2
+          list_head->prev == element2
+	  element2->prev == list_head
+	  element2->next == NULL
+
+     4) with N entries in the list:
+          list_head->next == element2
+          list_head->prev == elementN
+	  elementN->prev == element{N-1}
+	  elementN->next == NULL
+
+  This allows us to find the tail of the list by using
+  list_head->prev, which means we can add to the end of the list in
+  O(1) time
+
+
+  Note that the 'type' arguments below are no longer needed, but
+  are kept for now to prevent an incompatible argument change
+ */
+
+
+/*
+   add an element at the front of a list
+*/
+#define DLIST_ADD(list, p) \
+do { \
+        if (!(list)) { \
+		(p)->prev = (list) = (p);  \
+		(p)->next = NULL; \
+	} else { \
+		(p)->prev = (list)->prev; \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(list) = (p); \
+	} \
+} while (0)
+
+/*
+   remove an element from a list
+   Note that the element doesn't have to be in the list. If it
+   isn't then this is a no-op
+*/
+#define DLIST_REMOVE(list, p) \
+do { \
+	if ((p) == (list)) { \
+		if ((p)->next) (p)->next->prev = (p)->prev; \
+		(list) = (p)->next; \
+	} else if ((list) && (p) == (list)->prev) {	\
+		(p)->prev->next = NULL; \
+		(list)->prev = (p)->prev; \
+	} else { \
+		if ((p)->prev) (p)->prev->next = (p)->next; \
+		if ((p)->next) (p)->next->prev = (p)->prev; \
+	} \
+	if ((p) != (list)) (p)->next = (p)->prev = NULL;	\
+} while (0)
+
+/*
+   find the head of the list given any element in it.
+   Note that this costs O(N), so you should avoid this macro
+   if at all possible!
+*/
+#define DLIST_HEAD(p, result_head) \
+do { \
+       (result_head) = (p); \
+       while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
+} while(0)
+
+/* return the last element in the list */
+#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
+
+/* return the previous element in the list. */
+#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+   this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+        if (!(list) || !(el)) { \
+		DLIST_ADD(list, p); \
+	} else { \
+		(p)->prev = (el);   \
+		(p)->next = (el)->next;		\
+		(el)->next = (p);		\
+		if ((p)->next) (p)->next->prev = (p);	\
+		if ((list)->prev == (el)) (list)->prev = (p); \
+	}\
+} while (0)
+
+
+/*
+   add to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_ADD_END(list, p, type)			\
+do { \
+	if (!(list)) { \
+		DLIST_ADD(list, p); \
+	} else { \
+		DLIST_ADD_AFTER(list, p, (list)->prev); \
+	} \
+} while (0)
+
+/* promote an element to the from of a list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+          DLIST_REMOVE(list, p); \
+          DLIST_ADD(list, p); \
+} while (0)
+
+/*
+   demote an element to the end of a list.
+   Note that 'type' is ignored
+*/
+#define DLIST_DEMOTE(list, p, type)			\
+do { \
+	DLIST_REMOVE(list, p); \
+	DLIST_ADD_END(list, p, NULL);		\
+} while (0)
+
+/*
+   concatenate two lists - putting all elements of the 2nd list at the
+   end of the first list.
+   Note that 'type' is ignored
+*/
+#define DLIST_CONCATENATE(list1, list2, type)	\
+do { \
+	if (!(list1)) { \
+		(list1) = (list2); \
+	} else { \
+		(list1)->prev->next = (list2); \
+		if (list2) { \
+			void *_tmplist = (void *)(list1)->prev; \
+			(list1)->prev = (list2)->prev; \
+			(list2)->prev = _tmplist; \
+		} \
+	} \
+} while (0)
+
+#endif /* _DLINKLIST_H */

+ 164 - 0
ccan/iscsi/init.c

@@ -0,0 +1,164 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "dlinklist.h"
+
+
+struct iscsi_context *iscsi_create_context(const char *initiator_name)
+{
+	struct iscsi_context *iscsi;
+
+	iscsi = malloc(sizeof(struct iscsi_context));
+	if (iscsi == NULL) {
+		printf("Failed to allocate iscsi context\n");
+		return NULL;
+	}
+
+	bzero(iscsi, sizeof(struct iscsi_context));
+
+	iscsi->initiator_name = strdup(initiator_name);
+	if (iscsi->initiator_name == NULL) {
+		printf("Failed to allocate initiator name context\n");
+		free(iscsi);
+		return NULL;
+	}
+
+	iscsi->fd = -1;
+
+	/* use a "random" isid */
+	srandom(getpid() ^ time(NULL));
+	iscsi_set_random_isid(iscsi);
+
+	return iscsi;
+}
+
+int iscsi_set_random_isid(struct iscsi_context *iscsi)
+{
+	iscsi->isid[0] = 0x80;
+	iscsi->isid[1] = random()&0xff;
+	iscsi->isid[2] = random()&0xff;
+	iscsi->isid[3] = random()&0xff;
+	iscsi->isid[4] = 0;
+	iscsi->isid[5] = 0;
+
+	return 0;
+}
+
+int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
+{
+	if (iscsi == NULL) {
+		printf("Context is NULL when adding alias\n");
+		return -1;
+	}
+	if (iscsi->is_loggedin != 0) {
+		printf("Already logged in when adding alias\n");
+		return -2;
+	}
+
+	if (iscsi->alias != NULL) {
+		free(discard_const(iscsi->alias));
+		iscsi->alias = NULL;
+	}
+
+	iscsi->alias = strdup(alias);
+	if (iscsi->alias == NULL) {
+		printf("Failed to allocate alias name\n");
+		return -3;
+	}
+
+	return 0;
+}
+
+int iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
+{
+	if (iscsi == NULL) {
+		printf("Context is NULL when adding targetname\n");
+		return -1;
+	}
+	if (iscsi->is_loggedin != 0) {
+		printf("Already logged in when adding targetname\n");
+		return -2;
+	}
+
+	if (iscsi->target_name != NULL) {
+		free(discard_const(iscsi->target_name));
+		iscsi->target_name= NULL;
+	}
+
+	iscsi->target_name = strdup(target_name);
+	if (iscsi->target_name == NULL) {
+		printf("Failed to allocate target name\n");
+		return -3;
+	}
+
+	return 0;
+}
+
+int iscsi_destroy_context(struct iscsi_context *iscsi)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi == NULL) {
+		return 0;
+	}
+	if (iscsi->initiator_name != NULL) {
+		free(discard_const(iscsi->initiator_name));
+		iscsi->initiator_name = NULL;
+	}
+	if (iscsi->alias != NULL) {
+		free(discard_const(iscsi->alias));
+		iscsi->alias = NULL;
+	}
+	if (iscsi->is_loggedin != 0) {
+		printf("deswtroying context while logged in\n");
+	}
+	if (iscsi->fd != -1) {
+		iscsi_disconnect(iscsi);
+	}
+
+	if (iscsi->inbuf != NULL) {
+		free(iscsi->inbuf);
+		iscsi->inbuf = NULL;
+		iscsi->insize = 0;
+		iscsi->inpos = 0;
+	}
+
+	while ((pdu = iscsi->outqueue)) {
+	      	DLIST_REMOVE(iscsi->outqueue, pdu);
+		pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
+		iscsi_free_pdu(iscsi, pdu);
+	}
+	while ((pdu = iscsi->waitpdu)) {
+	      	DLIST_REMOVE(iscsi->waitpdu, pdu);
+		pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
+		iscsi_free_pdu(iscsi, pdu);
+	}
+
+	free(iscsi);
+
+	return 0;
+}

+ 143 - 0
ccan/iscsi/iscsi-private.h

@@ -0,0 +1,143 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+
+#ifndef _U_
+#define _U_
+#endif
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#endif
+
+struct iscsi_context {
+       const char *initiator_name;
+       const char *target_name;
+       const char *alias;
+       enum iscsi_session_type session_type;
+       unsigned char isid[6];
+       uint32_t itt;
+       uint32_t cmdsn;
+       uint32_t statsn;
+
+       int fd;
+       int is_connected;
+       int is_loggedin;
+
+       iscsi_command_cb connect_cb;
+       void *connect_data;
+
+       struct iscsi_pdu *outqueue;
+       struct iscsi_pdu *waitpdu;
+
+       int insize;
+       int inpos;
+       unsigned char *inbuf;
+};
+
+#define ISCSI_HEADER_SIZE			48
+
+#define ISCSI_PDU_IMMEDIATE		       0x40
+
+#define ISCSI_PDU_TEXT_FINAL		       0x80
+#define ISCSI_PDU_TEXT_CONTINUE		       0x40
+
+#define ISCSI_PDU_LOGIN_TRANSIT		       0x80
+#define ISCSI_PDU_LOGIN_CONTINUE	       0x40
+#define ISCSI_PDU_LOGIN_CSG_SECNEG	       0x00
+#define ISCSI_PDU_LOGIN_CSG_OPNEG	       0x04
+#define ISCSI_PDU_LOGIN_CSG_FF		       0x0c
+#define ISCSI_PDU_LOGIN_NSG_SECNEG	       0x00
+#define ISCSI_PDU_LOGIN_NSG_OPNEG	       0x01
+#define ISCSI_PDU_LOGIN_NSG_FF		       0x03
+
+
+#define ISCSI_PDU_SCSI_FINAL		       0x80
+#define ISCSI_PDU_SCSI_READ		       0x40
+#define ISCSI_PDU_SCSI_WRITE		       0x20
+#define ISCSI_PDU_SCSI_ATTR_UNTAGGED	       0x00
+#define ISCSI_PDU_SCSI_ATTR_SIMPLE	       0x01
+#define ISCSI_PDU_SCSI_ATTR_ORDERED	       0x02
+#define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE	       0x03
+#define ISCSI_PDU_SCSI_ATTR_ACA		       0x04
+
+#define ISCSI_PDU_DATA_FINAL		       0x80
+#define ISCSI_PDU_DATA_ACK_REQUESTED	       0x40
+#define ISCSI_PDU_DATA_BIDIR_OVERFLOW  	       0x10
+#define ISCSI_PDU_DATA_BIDIR_UNDERFLOW         0x08
+#define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW       0x04
+#define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW      0x02
+#define ISCSI_PDU_DATA_CONTAINS_STATUS	       0x01
+
+enum iscsi_opcode {ISCSI_PDU_NOP_OUT=0x00,
+     		  ISCSI_PDU_SCSI_REQUEST=0x01,
+     		  ISCSI_PDU_LOGIN_REQUEST=0x03,
+		  ISCSI_PDU_TEXT_REQUEST=0x04,
+		  ISCSI_PDU_LOGOUT_REQUEST=0x06,
+		  ISCSI_PDU_NOP_IN=0x20,
+     		  ISCSI_PDU_SCSI_RESPONSE=0x21,
+     		  ISCSI_PDU_LOGIN_RESPONSE=0x23,
+     		  ISCSI_PDU_TEXT_RESPONSE=0x24,
+     		  ISCSI_PDU_DATA_IN=0x25,
+     		  ISCSI_PDU_LOGOUT_RESPONSE=0x26};
+
+struct iscsi_pdu {
+       struct iscsi_pdu *prev, *next;
+
+       uint32_t itt;
+       uint32_t cmdsn;
+       enum iscsi_opcode response_opcode;
+
+       iscsi_command_cb callback;
+       void *private_data;
+
+       int written;
+       struct iscsi_data outdata;
+       struct iscsi_data indata;
+
+       struct iscsi_scsi_cbdata *scsi_cbdata;
+};
+
+void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata);
+
+struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode);
+void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags);
+void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu);
+void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt);
+void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn);
+void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun);
+void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn);
+void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen);
+int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize);
+int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment);
+int iscsi_set_random_isid(struct iscsi_context *iscsi);
+
+struct scsi_task;
+void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
+
+int iscsi_get_pdu_size(const unsigned char *hdr);
+int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size);
+
+int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
+int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished);
+int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);

+ 262 - 0
ccan/iscsi/iscsi.h

@@ -0,0 +1,262 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+struct iscsi_context;
+struct sockaddr;
+
+
+/*
+ * Returns the file descriptor that libiscsi uses.
+ */
+int iscsi_get_fd(struct iscsi_context *iscsi);
+
+/*
+ * Returns which events that we need to poll for for the iscsi file descriptor.
+ */
+int iscsi_which_events(struct iscsi_context *iscsi);
+
+/*
+ * Called to process the events when events become available for the iscsi file descriptor.
+ */
+int iscsi_service(struct iscsi_context *iscsi, int revents);
+
+
+
+/*
+ * Create a context for an ISCSI session.
+ * Initiator_name is the iqn name we want to identify to the target as.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+struct iscsi_context *iscsi_create_context(const char *initiator_name);
+
+/*
+ * Destroy an existing ISCSI context and tear down any existing connection.
+ * Callbacks for any command in flight will be invoked with ISCSI_STATUS_CANCELLED.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_destroy_context(struct iscsi_context *iscsi);
+
+/*
+ * Set an optional alias name to identify with when connecting to the target
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
+
+/*
+ * Set the iqn name of the taqget to login to.
+ * The target name must be set before a normal-login can be initiated.
+ * Only discovery-logins are possible without setting the target iqn name.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
+
+
+/* Types of icsi sessions. Discovery sessions are used to query for what targets exist behind
+ * the portal connected to. Normal sessions are used to log in and do I/O to the SCSI LUNs
+ */
+enum iscsi_session_type {ISCSI_SESSION_DISCOVERY=1, ISCSI_SESSION_NORMAL=2};
+
+/*
+ * Set the session type for a scsi context.
+ * Session type can only be set/changed while the iscsi context is not logged in to
+ * a target.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type);
+
+
+/* ISCSI_STATUS_GOOD must map to SCSI_STATUS_GOOD
+ * and ISCSI_STATUS_CHECK_CONDITION must map to SCSI_STATUS_CHECK_CONDITION
+ */
+enum icsi_status { ISCSI_STATUS_GOOD		=0,
+     		   ISCSI_STATUS_CHECK_CONDITION	=2,
+		   ISCSI_STATUS_CANCELLED	=0x0f000000,
+		   ISCSI_STATUS_ERROR		=0x0f000001 };
+
+
+/* 
+ * Generic callback for completion of iscsi_*_async().
+ * command_data depends on status.
+ */
+typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status, void *command_data, void *private_data);
+
+
+/*
+ * Asynchronous call to connect a TCP connection to the target-host/port
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of the connection will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * This command is unique in that the callback can be invoked twice.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Connection was successful. Command_data is NULL.
+ *                            In this case the callback will be invoked a second time once the connection
+ *                            is torn down.
+ * 
+ *    ISCSI_STATUS_ERROR    : Either failed to establish the connection, or an already established connection
+ *                            has failed with an error.
+ *
+ * The callback will NOT be invoked if the session is explicitely torn down through a call to
+ * iscsi_disconnect() or iscsi_destroy_context().
+ */
+int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data);
+
+/*
+ * Disconnect a connection to a target.
+ * You can not disconnect while being logged in to a target.
+ *
+ * Returns:
+ *  0 disconnect was successful
+ * <0 error
+ */
+int iscsi_disconnect(struct iscsi_context *iscsi);
+
+/*
+ * Asynchronous call to perform an ISCSI login.
+ *
+ * Returns:
+ *  0 if the call was initiated and a login will be attempted. Result of the login will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : login was successful. Command_data is always NULL.
+ *    ISCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL.
+ *    ISCSI_STATUS_ERROR    : login failed. Command_data is NULL.
+ */
+int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+
+/*
+ * Asynchronous call to perform an ISCSI logout.
+ *
+ * Returns:
+ *  0 if the call was initiated and a logout will be attempted. Result of the logout will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : logout was successful. Command_data is always NULL.
+ *    ISCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL.
+ */
+int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+
+/*
+ * Asynchronous call to perform an ISCSI discovery.
+ *
+ * discoveries can only be done on connected and logged in discovery sessions.
+ *
+ * Returns:
+ *  0 if the call was initiated and a discovery  will be attempted. Result of the logout will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Discovery was successful. Command_data is a pointer to a
+ *                            iscsi_discovery_address list of structures.
+ *                            This list of structures is only valid for the duration of the
+ *                            callback and all data will be freed once the callback returns.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
+
+struct iscsi_discovery_address {
+       struct iscsi_discovery_address *next;
+       const char *target_name;
+       const char *target_address;
+};
+
+/*
+ * Asynchronous call to perform an ISCSI NOP-OUT call
+ *
+ * Returns:
+ *  0 if the call was initiated and a nop-out will be attempted. Result will be reported
+ *    through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : NOP-OUT was successful and the server responded with a NOP-IN
+ *                            callback_data is a iscsi_data structure containing the data returned from
+ *                            the server.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data);
+
+
+/* These are the possible status values for the callbacks for scsi commands.
+ * The content of command_data depends on the status type.
+ *
+ * status :
+ *   ISCSI_STATUS_GOOD the scsi command completed successfullt on the target.
+ *   If this scsi command returns DATA-IN, that data is stored in an scsi_task structure 
+ *   returned in the command_data parameter. This buffer will be automatically freed once the callback
+ *   returns.
+ *
+ *   ISCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense.
+ *   Command_data contains a struct scsi_task. When the callback returns, this buffer
+ *   will automatically become freed.
+ *
+ *   ISCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is NULL.
+ *
+ *   ISCSI_STATUS_ERROR the command failed. Command_data is NULL.
+ */
+
+
+
+struct iscsi_data {
+       int size;
+       unsigned char *data;
+};
+
+
+/*
+ * Async commands for SCSI
+ */
+int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data);
+int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data);
+int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data);
+int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data);
+int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data);
+int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data);
+int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data);
+
+
+

+ 292 - 0
ccan/iscsi/login.c

@@ -0,0 +1,292 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	char *str;
+	int ret;
+
+	if (iscsi == NULL) {
+		printf("trying to login on NULL context\n");
+		return -1;
+	}
+
+	if (iscsi->is_loggedin != 0) {
+		printf("trying to login while already logged in\n");
+		return -2;
+	}
+
+	switch (iscsi->session_type) {
+	case ISCSI_SESSION_DISCOVERY:
+	case ISCSI_SESSION_NORMAL:
+		break;
+	default:
+		printf("trying to login without setting session type\n");
+		return -3;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST, ISCSI_PDU_LOGIN_RESPONSE);
+	if (pdu == NULL) {
+		printf("Failed to allocate login pdu\n");
+		return -4;
+	}
+
+	/* login request */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT|ISCSI_PDU_LOGIN_CSG_OPNEG|ISCSI_PDU_LOGIN_NSG_FF);
+
+
+	/* initiator name */
+	if (asprintf(&str, "InitiatorName=%s", iscsi->initiator_name) == -1) {
+		printf("asprintf failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -5;
+	}
+	ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+	free(str);
+	if (ret != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -6;
+	}
+
+	/* optional alias */
+	if (iscsi->alias) {
+		if (asprintf(&str, "InitiatorAlias=%s", iscsi->alias) == -1) {
+			printf("asprintf failed\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -7;
+		}
+		ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+		free(str);
+		if (ret != 0) {
+			printf("pdu add data failed\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -8;
+		}
+	}
+
+	/* target name */
+	if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
+		if (iscsi->target_name == NULL) {
+			printf("trying normal connect but target name not set\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -9;
+		}
+
+		if (asprintf(&str, "TargetName=%s", iscsi->target_name) == -1) {
+			printf("asprintf failed\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -10;
+		}
+		ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
+		free(str);
+		if (ret != 0) {
+			printf("pdu add data failed\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -11;
+		}
+	}
+
+	/* session type */
+	switch (iscsi->session_type) {
+	case ISCSI_SESSION_DISCOVERY:
+		str = "SessionType=Discovery";
+		break;
+	case ISCSI_SESSION_NORMAL:
+		str = "SessionType=Normal";
+		break;
+	default:
+		printf("can not handle sessions %d yet\n", iscsi->session_type);
+		return -12;
+	}
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -13;
+	}
+
+	str = "HeaderDigest=None";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -14;
+	}
+	str = "DataDigest=None";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -15;
+	}
+	str = "InitialR2T=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -16;
+	}
+	str = "ImmediateData=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -17;
+	}
+	str = "MaxBurstLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -18;
+	}
+	str = "FirstBurstLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -19;
+	}
+	str = "MaxRecvDataSegmentLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -20;
+	}
+	str = "DataPDUInOrder=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -21;
+	}
+	str = "DataSequenceInOrder=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
+		printf("pdu add data failed\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -22;
+	}
+
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		printf("failed to queue iscsi login pdu\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -23;
+	}
+
+	return 0;
+}
+
+int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+	int status;
+
+	if (size < ISCSI_HEADER_SIZE) {
+		printf("dont have enough data to read status from login reply\n");
+		return -1;
+	}
+
+	/* XXX here we should parse the data returned in case the target renegotiated some
+	 *     some parameters.
+	 *     we should also do proper handshaking if the target is not yet prepared to transition
+	 *     to the next stage
+	 */ 
+	status = ntohs(*(uint16_t *)&hdr[36]);
+	if (status != 0) {
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
+		return 0;
+	}
+
+	iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
+
+	iscsi->is_loggedin = 1;
+	pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+	return 0;
+}
+
+
+int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi == NULL) {
+		printf("trying to logout on NULL context\n");
+		return -1;
+	}
+
+	if (iscsi->is_loggedin == 0) {
+		printf("trying to logout while not logged in\n");
+		return -2;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST, ISCSI_PDU_LOGOUT_RESPONSE);
+	if (pdu == NULL) {
+		printf("Failed to allocate logout pdu\n");
+		return -3;
+	}
+
+	/* logout request has the immediate flag set */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags : close the session */
+	iscsi_pdu_set_pduflags(pdu, 0x80);
+
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		printf("failed to queue iscsi logout pdu\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+
+	return 0;
+}
+
+int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size _U_)
+{
+	iscsi->is_loggedin = 0;
+	pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+	return 0;
+}
+
+int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type)
+{
+	if (iscsi == NULL) {
+		printf("Trying to set sesssion type on NULL context\n");
+		return -1;
+	}
+	if (iscsi->is_loggedin) {
+		printf("trying to set session type while logged in\n");
+		return -2;
+	}
+	
+	iscsi->session_type = session_type;
+
+	return 0;
+}

+ 92 - 0
ccan/iscsi/nop.c

@@ -0,0 +1,92 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi == NULL) {
+		printf("trying to send nop-out on NULL context\n");
+		return -1;
+	}
+
+	if (iscsi->is_loggedin == 0) {
+		printf("trying send nop-out while not logged in\n");
+		return -2;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
+	if (pdu == NULL) {
+		printf("Failed to allocate nop-out pdu\n");
+		return -3;
+	}
+
+	/* immediate flag */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, 0x80);
+
+	/* ttt */
+	iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+	/* lun */
+	iscsi_pdu_set_lun(pdu, 2);
+
+	/* cmdsn is not increased if Immediate delivery*/
+	iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+	pdu->cmdsn = iscsi->cmdsn;
+//	iscsi->cmdsn++;
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
+		printf("Failed to add outdata to nop-out\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		printf("failed to queue iscsi nop-out pdu\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -5;
+	}
+
+	return 0;
+}
+
+int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+	struct iscsi_data data;
+
+	data.data = NULL;
+	data.size = 0;
+
+	if (size > ISCSI_HEADER_SIZE) {
+		data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
+		data.size = size - ISCSI_HEADER_SIZE;
+	}
+	pdu->callback(iscsi, ISCSI_STATUS_GOOD, &data, pdu->private_data);
+
+	return 0;
+}

+ 302 - 0
ccan/iscsi/pdu.c

@@ -0,0 +1,302 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode)
+{
+	struct iscsi_pdu *pdu;
+
+	pdu = malloc(sizeof(struct iscsi_pdu));
+	if (pdu == NULL) {
+		printf("failed to allocate pdu\n");
+		return NULL;
+	}
+	bzero(pdu, sizeof(struct iscsi_pdu));
+
+	pdu->outdata.size = ISCSI_HEADER_SIZE;
+	pdu->outdata.data = malloc(pdu->outdata.size);
+
+	if (pdu->outdata.data == NULL) {
+		printf("failed to allocate pdu header\n");
+		free(pdu);
+		pdu = NULL;
+		return NULL;
+	}
+	bzero(pdu->outdata.data, pdu->outdata.size);
+
+	/* opcode */
+	pdu->outdata.data[0] = opcode;
+	pdu->response_opcode = response_opcode;
+
+	/* isid */
+	if (opcode ==ISCSI_PDU_LOGIN_REQUEST) {
+		memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
+	}
+
+	/* itt */
+	*(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
+	pdu->itt = iscsi->itt;
+
+	iscsi->itt++;
+
+	return pdu;
+}
+
+void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+	if (pdu == NULL) {
+		printf("trying to free NULL pdu\n");
+		return;
+	}
+	if (pdu->outdata.data) {
+		free(pdu->outdata.data);
+		pdu->outdata.data = NULL;
+	}
+	if (pdu->indata.data) {
+		free(pdu->indata.data);
+		pdu->indata.data = NULL;
+	}
+	if (pdu->scsi_cbdata) {
+		iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
+		pdu->scsi_cbdata = NULL;
+	}
+
+	free(pdu);
+}
+
+
+int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment)
+{
+	int len, aligned;
+	unsigned char *buf;
+
+	if (dsize == 0) {
+		printf("Trying to append zero size data to iscsi_data\n");
+		return -1;
+	}
+
+	len = data->size + dsize;
+	aligned = len;
+	if (pdualignment) {
+		aligned = (aligned+3)&0xfffffffc;
+	}
+	buf = malloc(aligned);
+	if (buf == NULL) {	
+		printf("failed to allocate buffer for %d bytes\n", len);
+		return -2;
+	}
+
+	memcpy(buf, data->data, data->size);
+	memcpy(buf + data->size, dptr, dsize);
+	if (len != aligned) {
+		/* zero out any padding at the end */
+	       bzero(buf+len, aligned-len);
+	}
+
+	free(data->data);
+	data->data  = buf;
+	data->size = len;
+
+	return 0;
+}
+
+int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize)
+{
+	int len, aligned;
+	unsigned char *buf;
+ 
+	if (pdu == NULL) {
+		printf("trying to add data to NULL pdu\n");
+		return -1;
+	}
+	if (dsize == 0) {
+		printf("Trying to append zero size data to pdu\n");
+		return -2;
+	}
+
+	if (iscsi_add_data(&pdu->outdata, dptr, dsize, 1) != 0) {
+		printf("failed to add data to pdu buffer\n");
+		return -3;
+	}
+
+	/* update data segment length */
+	*(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size-ISCSI_HEADER_SIZE);
+
+	return 0;
+}
+
+int iscsi_get_pdu_size(const unsigned char *hdr)
+{
+	int size;
+
+	size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
+	size = (size+3)&0xfffffffc;
+
+	return size;
+}
+
+
+int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size)
+{
+	uint32_t itt;
+	enum iscsi_opcode opcode;
+	struct iscsi_pdu *pdu;
+	uint8_t	ahslen;
+
+	opcode = hdr[0] & 0x3f;
+	ahslen = hdr[4];
+	itt = ntohl(*(uint32_t *)&hdr[16]);
+
+	if (ahslen != 0) {
+		printf("cant handle expanded headers yet\n");
+		return -1;
+	}
+
+	for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
+		enum iscsi_opcode expected_response = pdu->response_opcode;
+		int is_finished = 1;
+
+		if (pdu->itt != itt) {
+			continue;
+		}
+
+		/* we have a special case with scsi-command opcodes, the are replied to by either a scsi-response
+		 * or a data-in, or a combination of both.
+		 */
+		if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) {
+			expected_response = ISCSI_PDU_DATA_IN;
+		}
+				
+		if (opcode != expected_response) {
+			printf("Got wrong opcode back for itt:%d  got:%d expected %d\n", itt, opcode, pdu->response_opcode);
+			return -1;
+		}
+		switch (opcode) {
+		case ISCSI_PDU_LOGIN_RESPONSE:
+			if (iscsi_process_login_reply(iscsi, pdu, hdr, size) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi login reply failed\n");
+				return -2;
+			}
+			break;
+		case ISCSI_PDU_TEXT_RESPONSE:
+			if (iscsi_process_text_reply(iscsi, pdu, hdr, size) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi text reply failed\n");
+				return -2;
+			}
+			break;
+		case ISCSI_PDU_LOGOUT_RESPONSE:
+			if (iscsi_process_logout_reply(iscsi, pdu, hdr, size) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi logout reply failed\n");
+				return -3;
+			}
+			break;
+		case ISCSI_PDU_SCSI_RESPONSE:
+			if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi response reply failed\n");
+				return -4;
+			}
+			break;
+		case ISCSI_PDU_DATA_IN:
+			if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size, &is_finished) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi data in failed\n");
+				return -4;
+			}
+			break;
+		case ISCSI_PDU_NOP_IN:
+			if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size) != 0) {
+				DLIST_REMOVE(iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				printf("iscsi nop-in failed\n");
+				return -5;
+			}
+			break;
+		default:
+			printf("Dont know how to handle opcode %d\n", opcode);
+			return -2;
+		}
+
+		if (is_finished) {
+			DLIST_REMOVE(iscsi->waitpdu, pdu);
+			iscsi_free_pdu(iscsi, pdu);
+		} else {
+			printf("pdu is not yet finished, let it remain\n");
+		}
+		return 0;
+	}
+
+	return 0;
+}
+
+void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
+{
+	pdu->outdata.data[1] = flags;
+}
+
+void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
+{
+	pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
+}
+
+void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
+{
+	*(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
+}
+
+void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
+{
+	*(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
+}
+
+void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
+{
+	*(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
+}
+
+void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
+{
+	bzero(&pdu->outdata.data[32], 16);
+	memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
+}
+
+void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
+{
+	pdu->outdata.data[9] = lun;
+}
+
+void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
+{
+	*(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
+}

+ 436 - 0
ccan/iscsi/scsi-command.c

@@ -0,0 +1,436 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+struct iscsi_scsi_cbdata {
+       struct iscsi_scsi_cbdata *prev, *next;
+       iscsi_command_cb  callback;
+       void             *private_data;
+       struct scsi_task *task;
+};
+
+void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
+{
+	if (scsi_cbdata == NULL) {
+		return;
+	}
+	if (scsi_cbdata->task == NULL) {
+		scsi_free_scsi_task(scsi_cbdata->task);
+		scsi_cbdata->task = NULL;
+	}
+	free(scsi_cbdata);
+}
+
+static void iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct iscsi_scsi_cbdata *scsi_cbdata = (struct iscsi_scsi_cbdata *)private_data;
+	struct scsi_task *task = command_data;
+
+	switch (status) {
+	case ISCSI_STATUS_CHECK_CONDITION:
+		scsi_cbdata->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, scsi_cbdata->private_data);	
+		return;
+	
+
+	case ISCSI_STATUS_GOOD:
+		scsi_cbdata->callback(iscsi, ISCSI_STATUS_GOOD, task, scsi_cbdata->private_data);	
+		return;
+	default:
+		printf("Cant handle  scsi status %d yet\n", status);
+		scsi_cbdata->callback(iscsi, ISCSI_STATUS_ERROR, task, scsi_cbdata->private_data);	
+	}
+}
+
+
+static int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun, struct scsi_task *task, iscsi_command_cb cb, struct iscsi_data *data, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	struct iscsi_scsi_cbdata *scsi_cbdata;
+	int flags;
+
+	if (iscsi == NULL) {
+		printf("trying to send command on NULL context\n");
+		scsi_free_scsi_task(task);
+		return -1;
+	}
+
+	if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
+		printf("Trying to send command on discovery session\n");
+		scsi_free_scsi_task(task);
+		return -2;
+	}
+
+	if (iscsi->is_loggedin == 0) {
+		printf("Trying to send command while not logged in\n");
+		scsi_free_scsi_task(task);
+		return -3;
+	}
+
+	scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
+	if (scsi_cbdata == NULL) {
+		printf("failed to allocate scsi cbdata\n");
+		scsi_free_scsi_task(task);
+		return -4;
+	}
+	bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
+	scsi_cbdata->task         = task;
+	scsi_cbdata->callback     = cb;
+	scsi_cbdata->private_data = private_data;
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST, ISCSI_PDU_SCSI_RESPONSE);
+	if (pdu == NULL) {
+		printf("Failed to allocate text pdu\n");
+		iscsi_free_scsi_cbdata(scsi_cbdata);
+		return -5;
+	}
+	pdu->scsi_cbdata = scsi_cbdata;
+
+	/* flags */
+	flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
+	switch (task->xfer_dir) {
+	case SCSI_XFER_NONE:
+		break;
+	case SCSI_XFER_READ:
+		flags |= ISCSI_PDU_SCSI_READ;
+		break;
+	case SCSI_XFER_WRITE:
+		flags |= ISCSI_PDU_SCSI_WRITE;
+		if (data == NULL) {
+			printf("DATA-OUT command but data == NULL\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -5;
+		}
+		if (data->size != task->expxferlen) {
+			printf("data size:%d is not same as expected data transfer length:%d\n", data->size, task->expxferlen);
+			iscsi_free_pdu(iscsi, pdu);
+			return -7;
+		}
+		if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size) != 0) {
+			printf("Failed to add outdata to the pdu\n");
+			iscsi_free_pdu(iscsi, pdu);
+			return -6;
+		}
+
+		break;
+	}
+	iscsi_pdu_set_pduflags(pdu, flags);
+
+	/* lun */
+	iscsi_pdu_set_lun(pdu, lun);
+
+	/* expxferlen */
+	iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
+
+	/* cmdsn */
+	iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+	pdu->cmdsn = iscsi->cmdsn;
+	iscsi->cmdsn++;
+
+	/* exp statsn */
+	iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
+		
+	/* cdb */
+	iscsi_pdu_set_cdb(pdu, task);
+
+	pdu->callback     = iscsi_scsi_response_cb;
+	pdu->private_data = scsi_cbdata;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		printf("failed to queue iscsi scsi pdu\n");
+		iscsi_free_pdu(iscsi, pdu);
+		return -6;
+	}
+
+	return 0;
+}
+
+
+int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
+{
+	int statsn, flags, response, status;
+	struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+	struct scsi_task *task = scsi_cbdata->task;
+
+	statsn = ntohl(*(uint32_t *)&hdr[24]);
+	if (statsn > (int)iscsi->statsn) {
+		iscsi->statsn = statsn;
+	}
+
+	flags = hdr[1];
+	if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+		printf("scsi response pdu but Final bit is not set: 0x%02x\n", flags);
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+		return -1;
+	}
+	if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+		printf("scsi response asked for ACK 0x%02x\n", flags);
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+		return -1;
+	}
+	/* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
+	 * libiscsi to not generate under/over flows
+	 */
+	if ((flags&ISCSI_PDU_DATA_BIDIR_OVERFLOW) != 0) {
+		printf("scsi response contains bidir overflow 0x%02x\n", flags);
+	}
+	if ((flags&ISCSI_PDU_DATA_BIDIR_UNDERFLOW) != 0) {
+		printf("scsi response contains bidir underflow 0x%02x\n", flags);
+	}
+	if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
+		printf("scsi response contains residual overflow 0x%02x\n", flags);
+	}
+	if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
+		printf("scsi response contains residual underflow 0x%02x\n", flags);
+	}
+
+
+	response = hdr[2];
+	if (response != 0x00) {
+		printf("scsi reply response field:%d\n", response);
+	}
+
+	status = hdr[3];
+
+	switch (status) {
+	case ISCSI_STATUS_GOOD:
+		task->datain.data = pdu->indata.data;
+		task->datain.size = pdu->indata.size;
+
+		pdu->callback(iscsi, ISCSI_STATUS_GOOD, &pdu->indata, pdu->private_data);
+		break;
+	case ISCSI_STATUS_CHECK_CONDITION:
+		task->datain.data = discard_const(hdr + ISCSI_HEADER_SIZE);
+		task->datain.size = size - ISCSI_HEADER_SIZE;
+
+		task->sense.error_type = task->datain.data[2] & 0x7f;
+		task->sense.key        = task->datain.data[4] & 0x0f;
+		task->sense.ascq       = ntohs(*(uint16_t *)&(task->datain.data[14]));
+
+		pdu->callback(iscsi, ISCSI_STATUS_CHECK_CONDITION, task, pdu->private_data);	
+		break;
+	default:
+		printf("Unknown status :%d\n", status);
+
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);	
+		return -1;
+	}
+
+	return 0;
+}
+
+int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished)
+{
+	int statsn, flags, status;
+	struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+	struct scsi_task *task = scsi_cbdata->task;
+	int dsl;
+
+	statsn = ntohl(*(uint32_t *)&hdr[24]);
+	if (statsn > (int)iscsi->statsn) {
+		iscsi->statsn = statsn;
+	}
+
+	flags = hdr[1];
+	if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+		printf("scsi response asked for ACK 0x%02x\n", flags);
+		pdu->callback(iscsi, ISCSI_STATUS_ERROR, task, pdu->private_data);
+		return -1;
+	}
+	/* for now, we ignore all overflow/underflow flags. We just print/log them so we can tweak
+	 * libiscsi to not generate under/over flows
+	 */
+	if ((flags&ISCSI_PDU_DATA_RESIDUAL_OVERFLOW) != 0) {
+		printf("scsi response contains residual overflow 0x%02x\n", flags);
+	}
+	if ((flags&ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW) != 0) {
+		printf("scsi response contains residual underflow 0x%02x\n", flags);
+	}
+
+	dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
+
+	if (dsl > size - ISCSI_HEADER_SIZE) {
+		printf ("dsl is :%d, while buffser size if %d\n", dsl, size - ISCSI_HEADER_SIZE);
+	}
+
+	if (iscsi_add_data(&pdu->indata, discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0) != 0) {
+		printf("failed to add data to pdu in buffer\n");
+		return -3;
+	}
+
+
+	if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+		printf("scsi data-in without Final bit: 0x%02x\n", flags);
+		*is_finished = 0;
+	}
+	if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
+		printf("scsi data-in without Status bit: 0x%02x\n", flags);
+		*is_finished = 0;
+	}
+
+	if (*is_finished == 0) {
+		return 0;
+	}
+
+
+	/* this was the final data-in packet in the sequence and it has the s-bit set, so invoke the
+	 * callback.
+	 */
+	status = hdr[3];
+	task->datain.data = pdu->indata.data;
+	task->datain.size = pdu->indata.size;
+
+	pdu->callback(iscsi, status, task, pdu->private_data);
+
+	return 0;
+}
+
+
+
+
+/*
+ * SCSI commands
+ */
+
+int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if ((task = scsi_cdb_testunitready()) == NULL) {
+		printf("Failed to create testunitready cdb\n");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+	return ret;
+}
+
+
+int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if (alloc_len < 16) {
+		printf("Minimum allowed alloc len for reportluns is 16. You specified %d\n", alloc_len);
+		return -1;
+	}
+
+	if ((task = scsi_reportluns_cdb(report_type, alloc_len)) == NULL) {
+		printf("Failed to create reportluns cdb\n");
+		return -2;
+	}
+	/* report luns are always sent to lun 0 */
+	ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL, private_data);
+
+	return ret;
+}
+
+int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if ((task = scsi_cdb_inquiry(evpd, page_code, maxsize)) == NULL) {
+		printf("Failed to create inquiry cdb\n");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+	return ret;
+}
+
+int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if ((task = scsi_cdb_readcapacity10(lba, pmi)) == NULL) {
+		printf("Failed to create readcapacity10 cdb\n");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+	return ret;
+}
+
+int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if (datalen % blocksize != 0) {
+		printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
+		return -1;
+	}
+
+	if ((task = scsi_cdb_read10(lba, datalen, blocksize)) == NULL) {
+		printf("Failed to create read10 cdb\n");
+		return -2;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+	return ret;
+}
+
+
+int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data)
+{
+	struct scsi_task *task;
+	struct iscsi_data outdata;
+	int ret;
+
+	if (datalen % blocksize != 0) {
+		printf("datalen:%d is not a multiple of the blocksize:%d\n", datalen, blocksize);
+		return -1;
+	}
+
+	if ((task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize)) == NULL) {
+		printf("Failed to create read10 cdb\n");
+		return -2;
+	}
+
+	outdata.data = data;
+	outdata.size = datalen;
+
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, private_data);
+
+	return ret;
+}
+
+int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if ((task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code, alloc_len)) == NULL) {
+		printf("Failed to create modesense6 cdb\n");
+		return -2;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, private_data);
+
+	return ret;
+}
+

+ 463 - 0
ccan/iscsi/scsi-lowlevel.c

@@ -0,0 +1,463 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * would be nice if this could grow into a full blown library for scsi to 
+ * 1, build a CDB
+ * 2, check how big a complete data-in structure needs to be
+ * 3, unmarshall data-in into a real structure
+ * 4, marshall a real structure into a data-out blob
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include "scsi-lowlevel.h"
+#include "dlinklist.h"
+
+
+void scsi_free_scsi_task(struct scsi_task *task)
+{
+	struct scsi_allocated_memory *mem;
+
+	while((mem = task->mem)) {
+		   DLIST_REMOVE(task->mem, mem);
+		   free(mem);
+	}
+	free(task);
+}
+
+void *scsi_malloc(struct scsi_task *task, size_t size)
+{
+	struct scsi_allocated_memory *mem;
+
+	mem = malloc(sizeof(struct scsi_allocated_memory));
+	if (mem == NULL) {
+		printf("Failed to allocate memory to scsi task\n");
+		return NULL;
+	}
+	bzero(mem, sizeof(struct scsi_allocated_memory));
+	mem->ptr = malloc(size);
+	if (mem->ptr == NULL) {
+		printf("Failed to allocate memory buffer for scsi task\n");
+		free(mem);
+		return NULL;
+	}
+	bzero(mem->ptr, size);
+	DLIST_ADD(task->mem, mem);
+	return mem->ptr;
+}
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *scsi_cdb_testunitready(void)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_TESTUNITREADY;
+
+	task->cdb_size   = 6;
+	task->xfer_dir   = SCSI_XFER_NONE;
+	task->expxferlen = 0;
+
+	return task;
+}
+
+
+/*
+ * REPORTLUNS
+ */
+struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_REPORTLUNS;
+	task->cdb[2]   = report_type;
+	*(uint32_t *)&task->cdb[6] = htonl(alloc_len);
+
+	task->cdb_size = 12;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.reportluns.report_type = report_type;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full report luns datain structure
+ */
+static int scsi_reportluns_datain_getfullsize(struct scsi_task *task)
+{
+	uint32_t list_size;
+
+	list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+	
+	return list_size;
+}
+
+/*
+ * unmarshall the data in blob for reportluns into a structure
+ */
+static struct scsi_reportluns_list *scsi_reportluns_datain_unmarshall(struct scsi_task *task)
+{
+	struct scsi_reportluns_list *list;
+	int list_size;
+	int i, num_luns;
+
+	if (task->datain.size < 4) {
+		printf("not enough data for reportluns list length\n");
+		return NULL;
+	}
+
+	list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+	if (list_size < task->datain.size) {
+		printf("not enough data to unmarshall reportluns data\n");
+		return NULL;
+	}
+
+	num_luns = list_size / 8 - 1;
+	list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns) + sizeof(uint16_t) * num_luns);
+	if (list == NULL) {
+		printf("Failed to allocate reportluns structure\n");
+		return NULL;
+	}
+
+	list->num = num_luns;
+	for (i=0; i<num_luns; i++) {
+		list->luns[i] = htons(*(uint16_t *)&(task->datain.data[i*8+8]));
+	}
+	
+	return list;
+}	
+
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_READCAPACITY10;
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+
+	if (pmi) {
+		task->cdb[8] |= 0x01;
+	}
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = 8;
+
+	task->params.readcapacity10.lba = lba;
+	task->params.readcapacity10.pmi = pmi;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full readcapacity10 datain structure
+ */
+static int scsi_readcapacity10_datain_getfullsize(struct scsi_task *task _U_)
+{
+	return 8;
+}
+
+/*
+ * unmarshall the data in blob for readcapacity10 into a structure
+ */
+static struct scsi_readcapacity10 *scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
+{
+	struct scsi_readcapacity10 *rc10;
+
+	if (task->datain.size < 8) {
+		printf("Not enough data to unmarshall readcapacity10\n");
+		return NULL;
+	}
+	rc10 = malloc(sizeof(struct scsi_readcapacity10));
+	if (rc10 == NULL) {
+		printf("Failed to allocate readcapacity10 structure\n");
+		return NULL;
+	}
+
+	rc10->lba        = htonl(*(uint32_t *)&(task->datain.data[0]));
+	rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
+
+	return rc10;
+}
+
+
+
+
+
+/*
+ * INQUIRY
+ */
+struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_INQUIRY;
+
+	if (evpd) {
+		task->cdb[1] |= 0x01;
+	}
+
+	task->cdb[2] = page_code;
+
+	*(uint16_t *)&task->cdb[3] = htons(alloc_len);
+
+	task->cdb_size = 6;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.inquiry.evpd      = evpd;
+	task->params.inquiry.page_code = page_code;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full inquiry datain structure
+ */
+static int scsi_inquiry_datain_getfullsize(struct scsi_task *task)
+{
+	if (task->params.inquiry.evpd != 0) {
+		printf("Can not handle extended inquiry yet\n");
+		return -1;
+	}
+
+	/* standard inquiry*/
+	return task->datain.data[4] + 3;
+}
+
+/*
+ * unmarshall the data in blob for inquiry into a structure
+ */
+void *scsi_inquiry_datain_unmarshall(struct scsi_task *task)
+{
+	struct scsi_inquiry_standard *inq;
+
+	if (task->params.inquiry.evpd != 0) {
+		printf("Can not handle extended inquiry yet\n");
+		return NULL;
+	}
+
+	/* standard inquiry */
+	inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
+	if (inq == NULL) {
+		printf("Failed to allocate standard inquiry structure\n");
+		return NULL;
+	}
+
+       inq->periperal_qualifier    = (task->datain.data[0]>>5)&0x07;
+       inq->periperal_device_type  = task->datain.data[0]&0x1f;
+       inq->rmb                    = task->datain.data[1]&0x80;
+       inq->version                = task->datain.data[2];
+       inq->normaca                = task->datain.data[3]&0x20;
+       inq->hisup                  = task->datain.data[3]&0x10;
+       inq->response_data_format   = task->datain.data[3]&0x0f;
+
+       memcpy(&inq->vendor_identification[0], &task->datain.data[8], 8);
+       memcpy(&inq->product_identification[0], &task->datain.data[16], 16);
+       memcpy(&inq->product_revision_level[0], &task->datain.data[32], 4);
+
+       return inq;
+}
+
+/*
+ * READ10
+ */
+struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_READ10;
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+	*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = xferlen;
+
+	return task;
+}
+
+/*
+ * WRITE10
+ */
+struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_WRITE10;
+
+	if (fua) {
+		task->cdb[1] |= 0x08;
+	}
+	if (fuanv) {
+		task->cdb[1] |= 0x02;
+	}
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+	*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_WRITE;
+	task->expxferlen = xferlen;
+
+	return task;
+}
+
+
+
+/*
+ * MODESENSE6
+ */
+struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		printf("Failed to allocate scsi task structure\n");
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_MODESENSE6;
+
+	if (dbd) {
+		task->cdb[1] |= 0x08;
+	}
+	task->cdb[2] = pc<<6 | page_code;
+	task->cdb[3] = sub_page_code;
+	task->cdb[4] = alloc_len;
+
+	task->cdb_size = 6;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.modesense6.dbd           = dbd;
+	task->params.modesense6.pc            = pc;
+	task->params.modesense6.page_code     = page_code;
+	task->params.modesense6.sub_page_code = sub_page_code;
+ 
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full report luns datain structure
+ */
+static int scsi_modesense6_datain_getfullsize(struct scsi_task *task)
+{
+	int len;
+
+	len = task->datain.data[0] + 1;
+
+	return len;
+}
+
+
+
+int scsi_datain_getfullsize(struct scsi_task *task)
+{
+	switch (task->cdb[0]) {
+	case SCSI_OPCODE_TESTUNITREADY:
+		return 0;
+	case SCSI_OPCODE_INQUIRY:
+		return scsi_inquiry_datain_getfullsize(task);
+	case SCSI_OPCODE_MODESENSE6:
+		return scsi_modesense6_datain_getfullsize(task);
+	case SCSI_OPCODE_READCAPACITY10:
+		return scsi_readcapacity10_datain_getfullsize(task);
+//	case SCSI_OPCODE_READ10:
+//	case SCSI_OPCODE_WRITE10:
+	case SCSI_OPCODE_REPORTLUNS:
+		return scsi_reportluns_datain_getfullsize(task);
+	}
+	printf("Unknown opcode:%d for datain get full size\n", task->cdb[0]);
+	return -1;
+}
+
+void *scsi_datain_unmarshall(struct scsi_task *task)
+{
+	switch (task->cdb[0]) {
+	case SCSI_OPCODE_TESTUNITREADY:
+		return NULL;
+	case SCSI_OPCODE_INQUIRY:
+		return scsi_inquiry_datain_unmarshall(task);
+	case SCSI_OPCODE_READCAPACITY10:
+		return scsi_readcapacity10_datain_unmarshall(task);
+//	case SCSI_OPCODE_READ10:
+//	case SCSI_OPCODE_WRITE10:
+	case SCSI_OPCODE_REPORTLUNS:
+		return scsi_reportluns_datain_unmarshall(task);
+	}
+	printf("Unknown opcode:%d for datain unmarshall\n", task->cdb[0]);
+	return NULL;
+}
+

+ 194 - 0
ccan/iscsi/scsi-lowlevel.h

@@ -0,0 +1,194 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _U_
+#define _U_
+#endif
+
+#define SCSI_CDB_MAX_SIZE			16
+
+enum scsi_opcode {SCSI_OPCODE_TESTUNITREADY=0x00,
+		  SCSI_OPCODE_INQUIRY=0x12,
+		  SCSI_OPCODE_MODESENSE6=0x1a,
+		  SCSI_OPCODE_READCAPACITY10=0x25,
+		  SCSI_OPCODE_READ10=0x28,
+		  SCSI_OPCODE_WRITE10=0x2A,
+		  SCSI_OPCODE_REPORTLUNS=0xA0};
+
+/* sense keys */
+#define SCSI_SENSE_KEY_ILLEGAL_REQUEST			0x05
+#define SCSI_SENSE_KEY_UNIT_ATTENTION			0x06
+
+/* ascq */
+#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB		0x2400
+#define SCSI_SENSE_ASCQ_BUS_RESET			0x2900
+
+enum scsi_xfer_dir {SCSI_XFER_NONE=0,
+     		    SCSI_XFER_READ=1,
+		    SCSI_XFER_WRITE=2};
+
+struct scsi_reportluns_params {
+       int report_type;
+};
+struct scsi_readcapacity10_params {
+       int lba;
+       int pmi;
+};
+struct scsi_inquiry_params {
+       int evpd;
+       int page_code;
+};
+struct scsi_modesense6_params {
+       int dbd;
+       int pc;
+       int page_code;
+       int sub_page_code;
+};
+
+struct scsi_sense {
+       unsigned char error_type;
+       unsigned char key;
+       int           ascq;
+};
+
+struct scsi_data {
+       int size;
+       unsigned char *data;
+};
+
+struct scsi_allocated_memory {
+       struct scsi_allocated_memory *prev, *next;
+       void *ptr;
+};
+
+struct scsi_task {
+       int cdb_size;
+       int xfer_dir;
+       int expxferlen;
+       unsigned char cdb[SCSI_CDB_MAX_SIZE];
+       union {
+       	     struct scsi_readcapacity10_params readcapacity10;
+       	     struct scsi_reportluns_params     reportluns;
+       	     struct scsi_inquiry_params        inquiry;
+	     struct scsi_modesense6_params     modesense6;
+       } params;
+
+       struct scsi_sense sense;
+       struct scsi_data datain;
+       struct scsi_allocated_memory *mem;
+};
+
+void scsi_free_scsi_task(struct scsi_task *task);
+
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *scsi_cdb_testunitready(void);
+
+
+/*
+ * REPORTLUNS
+ */
+#define SCSI_REPORTLUNS_REPORT_ALL_LUNS				0x00
+#define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY			0x01
+#define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY		0x02
+
+struct scsi_reportluns_list {
+       uint32_t num;
+       uint16_t luns[0];
+};
+
+struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len);
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_readcapacity10 {
+       uint32_t lba;
+       uint32_t block_size;
+};
+struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi);
+
+
+/*
+ * INQUIRY
+ */
+enum scsi_inquiry_peripheral_qualifier {SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED=0x00,
+     				        SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED=0x01,
+     				        SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED=0x03};
+
+enum scsi_inquiry_peripheral_device_type {SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS=0x00,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS=0x01,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER=0x02,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR=0x03,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE=0x04,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC=0x05,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER=0x06,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY=0x07,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER=0x08,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS=0x09,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER=0x0c,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES=0x0d,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS=0x0e,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER=0x0f,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER=0x10,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD=0x11,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION=0x12,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER=0x13,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN=0x1e,
+     					  SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN=0x1f};
+
+struct scsi_inquiry_standard {
+       enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+       enum scsi_inquiry_peripheral_device_type periperal_device_type;
+       int rmb;
+       int version;
+       int normaca;
+       int hisup;
+       int response_data_format;
+
+       char vendor_identification[8+1];
+       char product_identification[16+1];
+       char product_revision_level[4+1];
+}; 
+
+struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len);
+
+
+
+/*
+ * MODESENSE6
+ */
+enum scsi_modesense_page_control {SCSI_MODESENSE_PC_CURRENT=0x00,
+     				  SCSI_MODESENSE_PC_CHANGEABLE=0x01,
+				  SCSI_MODESENSE_PC_DEFAULT=0x02,
+				  SCSI_MODESENSE_PC_SAVED=0x03};
+
+enum scsi_modesense_page_code {SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES=0x3f};
+
+struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len);
+
+
+
+
+int scsi_datain_getfullsize(struct scsi_task *task);
+void *scsi_datain_unmarshall(struct scsi_task *task);
+
+struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize);
+struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize);
+

+ 329 - 0
ccan/iscsi/socket.c

@@ -0,0 +1,329 @@
+/* 
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "dlinklist.h"
+
+static void set_nonblocking(int fd)
+{
+	unsigned v;
+	v = fcntl(fd, F_GETFL, 0);
+        fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data)
+{
+	int tpgt = -1;
+	int port = 3260;
+	char *str;
+	char *addr;
+	struct sockaddr_storage s;
+	struct sockaddr_in *sin = (struct sockaddr_in *)&s;
+	int socksize;
+
+	if (iscsi == NULL) {
+		printf("Trying to connect NULL context\n");
+		return -1;
+	}
+	if (iscsi->fd != -1) {
+		printf("Trying to connect but already connected\n");
+		return -2;
+	}
+
+	addr = strdup(target);
+	if (addr == NULL) {
+		printf("failed to strdup target address\n");
+		return -3;
+	}
+	
+	/* check if we have a target portal group tag */
+	if ((str = rindex(addr, ',')) != NULL) {
+		tpgt = atoi(str+1);
+		str[0] = 0;
+	}
+
+	/* XXX need handling for {ipv6 addresses} */
+	/* for now, assume all is ipv4 */
+	if ((str = rindex(addr, ':')) != NULL) {
+		port = atoi(str+1);
+		str[0] = 0;
+	}
+
+	sin->sin_family = AF_INET;
+	sin->sin_port   = htons(port);
+	if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) {
+		printf("failed to convert to ip address\n");
+		free(addr);
+		return -4;
+	}
+	free(addr);
+
+	switch (s.ss_family) {
+	case AF_INET:
+		iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
+		socksize = sizeof(struct sockaddr_in);
+		break;
+	default:
+		printf("Unknown family :%d\n", s.ss_family);
+		return -5;
+
+	}
+
+	if (iscsi->fd == -1) {
+		printf("Failed to open socket\n");
+		return -6;
+
+	}
+
+	iscsi->connect_cb  = cb;
+	iscsi->connect_data = private_data;
+
+	set_nonblocking(iscsi->fd);
+
+	if (connect(iscsi->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) {
+		printf("Connect failed errno : %s (%d)\n", strerror(errno), errno);
+		return -7;
+	}		
+
+	return 0;
+}	    
+
+int iscsi_disconnect(struct iscsi_context *iscsi)
+{
+	if (iscsi == NULL) {
+		printf("Trying to disconnect NULL context\n");
+		return -1;
+	}
+	if (iscsi->is_loggedin != 0) {
+		printf("Trying to disconnect while logged in\n");
+		return -2;
+	}
+	if (iscsi->fd == -1) {
+		printf("Trying to disconnect but not connected\n");
+		return -3;
+	}
+
+	close(iscsi->fd);
+	iscsi->fd  = -1;
+
+	iscsi->is_connected = 0;
+
+	return 0;
+}
+
+int iscsi_get_fd(struct iscsi_context *iscsi)
+{
+	if (iscsi == NULL) {
+		printf("Trying to get fd for NULL context\n");
+		return -1;
+	}
+
+	return iscsi->fd;
+}
+
+int iscsi_which_events(struct iscsi_context *iscsi)
+{
+	int events = POLLIN;
+
+	if (iscsi->is_connected == 0) {
+		events |= POLLOUT;
+	}	
+
+	if (iscsi->outqueue) {
+		events |= POLLOUT;
+	}
+	return events;
+}
+
+static int iscsi_read_from_socket(struct iscsi_context *iscsi)
+{
+	int available;
+	int size;
+	unsigned char *buf;
+	ssize_t count;
+
+	if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
+		printf("ioctl FIONREAD returned error : %d\n", errno);
+		return -1;
+	}
+	if (available == 0) {
+		printf("no data readable in socket, socket is closed\n");
+		return -2;
+	}
+	size = iscsi->insize - iscsi->inpos + available;
+	buf = malloc(size);
+	if (buf == NULL) {
+		printf("failed to allocate %d bytes for input buffer\n", size);
+		return -3;
+	}
+	if (iscsi->insize > iscsi->inpos) {
+		memcpy(buf, iscsi->inbuf + iscsi->inpos, iscsi->insize - iscsi->inpos);
+		iscsi->insize -= iscsi->inpos;
+		iscsi->inpos   = 0;
+	}
+
+	count = read(iscsi->fd, buf + iscsi->insize, available);
+	if (count == -1) {
+		if (errno == EINTR) {
+			free(buf);
+			buf = NULL;
+			return 0;
+		}
+		printf("read from socket failed, errno:%d\n", errno);
+		free(buf);
+		buf = NULL;
+		return -4;
+	}
+
+	if (iscsi->inbuf != NULL) {
+		free(iscsi->inbuf);
+	}
+	iscsi->inbuf   = buf;
+	iscsi->insize += count;
+
+	while (1) {
+		if (iscsi->insize - iscsi->inpos < 48) {
+			return 0;
+		}
+		count = iscsi_get_pdu_size(iscsi->inbuf + iscsi->inpos);
+		if (iscsi->insize + iscsi->inpos < count) {
+			return 0;
+		}
+		if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos, count) != 0) {
+			printf("failed to process pdu\n");
+			return -5;
+		}
+		iscsi->inpos += count;
+		if (iscsi->inpos == iscsi->insize) {
+			free(iscsi->inbuf);
+			iscsi->inbuf = NULL;
+			iscsi->insize = 0;
+			iscsi->inpos = 0;
+		}
+		if (iscsi->inpos > iscsi->insize) {
+			printf("inpos > insize. bug!\n");
+			return -6;
+		}
+	}
+
+	return 0;
+}
+
+static int iscsi_write_to_socket(struct iscsi_context *iscsi)
+{
+	ssize_t count;
+
+	if (iscsi == NULL) {
+		printf("trying to write to socket for NULL context\n");
+		return -1;
+	}
+	if (iscsi->fd == -1) {
+		printf("trying to write but not connected\n");
+		return -2;
+	}
+
+	while (iscsi->outqueue != NULL) {
+		ssize_t total;
+
+		total = iscsi->outqueue->outdata.size;
+		total = (total +3) & 0xfffffffc;
+
+		count = write(iscsi->fd, iscsi->outqueue->outdata.data + iscsi->outqueue->written, total - iscsi->outqueue->written);
+		if (count == -1) {
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				printf("socket would block, return from write to socket\n");
+				return 0;
+			}
+			printf("Error when writing to socket :%d\n", errno);
+			return -3;
+		}
+
+		iscsi->outqueue->written += count;
+		if (iscsi->outqueue->written == total) {
+			struct iscsi_pdu *pdu = iscsi->outqueue;
+
+	       	    	DLIST_REMOVE(iscsi->outqueue, pdu);
+			DLIST_ADD_END(iscsi->waitpdu, pdu, NULL);
+		}
+	}
+	return 0;
+}
+
+int iscsi_service(struct iscsi_context *iscsi, int revents)
+{
+	if (revents & POLLERR) {
+		printf("iscsi_service: POLLERR, socket error\n");
+		iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
+		return -1;
+	}
+	if (revents & POLLHUP) {
+		printf("iscsi_service: POLLHUP, socket error\n");
+		iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
+		return -2;
+	}
+
+	if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
+		iscsi->is_connected = 1;
+		iscsi->connect_cb(iscsi, ISCSI_STATUS_GOOD, NULL, iscsi->connect_data);
+		return 0;
+	}
+
+	if (revents & POLLOUT && iscsi->outqueue != NULL) {
+		if (iscsi_write_to_socket(iscsi) != 0) {
+			printf("write to socket failed\n");
+			return -3;
+		}
+	}
+	if (revents & POLLIN) {
+		if (iscsi_read_from_socket(iscsi) != 0) {
+			printf("read from socket failed\n");
+			return -4;
+		}
+	}
+
+	return 0;
+}
+
+int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+	if (iscsi == NULL) {
+		printf("trying to queue to NULL context\n");
+		return -1;
+	}
+	if (pdu == NULL) {
+		printf("trying to queue NULL pdu\n");
+		return -2;
+	}
+	DLIST_ADD_END(iscsi->outqueue, pdu, NULL);
+
+	return 0;
+}
+
+
+

+ 430 - 0
ccan/iscsi/tools/iscsiclient.c

@@ -0,0 +1,430 @@
+/* This is an example of using libiscsi.
+ * It basically logs in to the the target and performs a discovery.
+ * It then selects the last target in the returned list and
+ * starts a normal login to that target.
+ * Once logged in it issues a REPORTLUNS call and selects the last returned lun in the list.
+ * This LUN is then used to send INQUIRY, READCAPACITY10 and READ10 test calls to.
+ */
+/* The reason why we have to specify an allocation length and sometimes probe, starting with a small value, probing how big the buffer 
+ * should be, and asking again with a bigger buffer.
+ * Why not just always ask with a buffer that is big enough?
+ * The reason is that a lot of scsi targets are "sensitive" and ""buggy""
+ * many targets will just fail the operation completely if they thing alloc len is unreasonably big.
+ */
+
+/* This is the host/port we connect to.*/
+#define TARGET "10.1.1.27:3260"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <poll.h>
+#include <ccan/iscsi/iscsi.h>
+#include <ccan/iscsi/scsi-lowlevel.h>
+
+struct client_state {
+       char *message;
+       int has_discovered_target;
+       char *target_name;
+       char *target_address;
+       int lun;
+       int block_size;
+};
+
+void nop_out_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct iscsi_data *data = command_data;
+
+	printf("NOP-IN status:%d\n", status);
+	if (data->size > 0) {
+		printf("NOP-IN data:%s\n", data->data);
+	}
+	exit(10);
+}
+
+
+void write10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	int i;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+
+		printf("Write10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		exit(10);
+	}
+
+	printf("Write successful\n");
+	exit(10);
+}
+
+
+void read10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	int i;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+		printf("Read10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		exit(10);
+	}
+
+	printf("READ10 successful. Block content:\n");
+	for (i=0;i<task->datain.size;i++) {
+		printf("%02x ", task->datain.data[i]);
+		if (i%16==15)
+			printf("\n");
+		if (i==69)
+			break;
+	}
+	printf("...\n");
+
+	printf("Finished,   wont try to write data since that will likely destroy your LUN :-(\n");
+	printf("Send NOP-OUT\n");
+	if (iscsi_nop_out_async(iscsi, nop_out_cb, "Ping!", 6, private_data) != 0) {
+		printf("failed to send nop-out\n");
+		exit(10);
+	}
+//	printf("write the block back\n");
+//	if (iscsi_write10_async(iscsi, clnt->lun, write10_cb, task->data.datain, task->datain.size, 0, 0, 0, clnt->block_size, private_data) != 0) {
+//		printf("failed to send write10 command\n");
+//		exit(10);
+//	}
+}
+
+void readcapacity10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	struct scsi_readcapacity10 *rc10;
+	int full_size;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+		printf("Readcapacity10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		exit(10);
+	}
+
+	full_size = scsi_datain_getfullsize(task);
+	if (full_size < task->datain.size) {
+		printf("not enough data for full size readcapacity10\n");
+		exit(10);
+	}
+
+	rc10 = scsi_datain_unmarshall(task);
+	if (rc10 == NULL) {
+		printf("failed to unmarshall readcapacity10 data\n");
+		exit(10);
+	}
+	clnt->block_size = rc10->block_size;
+	printf("READCAPACITY10 successful. Size:%d blocks  blocksize:%d. Read first block\n", rc10->lba, rc10->block_size);
+	free(rc10);
+
+	if (iscsi_read10_async(iscsi, clnt->lun, read10_cb, 0, clnt->block_size, clnt->block_size, private_data) != 0) {
+		printf("failed to send read10 command\n");
+		exit(10);
+	}
+}
+
+void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	int full_size;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+		printf("Modesense6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		exit(10);
+	}
+
+	full_size = scsi_datain_getfullsize(task);
+	if (full_size > task->datain.size) {
+		printf("did not get enough data for mode sense, sening modesense again asking for bigger buffer\n");
+		if (iscsi_modesense6_async(iscsi, clnt->lun, modesense6_cb, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, full_size, private_data) != 0) {
+			printf("failed to send modesense6 command\n");
+			exit(10);
+		}
+		return;
+	}
+
+	printf("MODESENSE6 successful.\n");
+	printf("Send READCAPACITY10\n");
+	if (iscsi_readcapacity10_async(iscsi, clnt->lun, readcapacity10_cb, 0, 0, private_data) != 0) {
+		printf("failed to send readcapacity command\n");
+		exit(10);
+	}
+}
+
+void inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	struct scsi_inquiry_standard *inq;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+		printf("Inquiry failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		exit(10);
+	}
+
+	printf("INQUIRY successful for standard data.\n");
+	inq = scsi_datain_unmarshall(task);
+	if (inq == NULL) {
+		printf("failed to unmarshall inquiry datain blob\n");
+		exit(10);
+	}
+
+	printf("Device Type is %d. VendorId:%s ProductId:%s\n", inq->periperal_device_type, inq->vendor_identification, inq->product_identification);
+	printf("Send MODESENSE6\n");
+	if (iscsi_modesense6_async(iscsi, clnt->lun, modesense6_cb, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, 4, private_data) != 0) {
+		printf("failed to send modesense6 command\n");
+		exit(10);
+	}
+
+}
+
+void testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+
+	if (status == ISCSI_STATUS_CHECK_CONDITION) {
+		printf("First testunitready failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
+		if (task->sense.key == SCSI_SENSE_KEY_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
+			printf("target device just came online, try again\n");
+
+			if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
+				printf("failed to send testunitready command\n");
+				exit(10);
+			}
+		}
+		return;
+	}
+
+	printf("TESTUNITREADY successful, do an inquiry on lun:%d\n", clnt->lun);
+	if (iscsi_inquiry_async(iscsi, clnt->lun, inquiry_cb, 0, 0, 64, private_data) != 0) {
+		printf("failed to send inquiry command\n");
+		exit(10);
+	}
+}
+
+
+void reportluns_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct scsi_task *task = command_data;
+	struct scsi_reportluns_list *list;
+	uint32_t full_report_size;
+	int i;
+
+	if (status != ISCSI_STATUS_GOOD) {
+		printf("Reportluns failed with unknown status code :%d\n", status);
+		return;
+	}
+
+	full_report_size = scsi_datain_getfullsize(task);
+
+	printf("REPORTLUNS status:%d   data size:%d,   full reports luns data size:%d\n", status, task->datain.size, full_report_size);
+	if (full_report_size > task->datain.size) {
+		printf("We did not get all the data we need in reportluns, ask again\n");
+		if (iscsi_reportluns_async(iscsi, reportluns_cb, 0, full_report_size, private_data) != 0) {
+			printf("failed to send reportluns command\n");
+			exit(10);
+		}
+		return;
+	}
+
+	
+	list = scsi_datain_unmarshall(task);
+	if (list == NULL) {
+		printf("failed to unmarshall reportluns datain blob\n");
+		exit(10);
+	}
+	for (i=0; i < list->num; i++) {
+		printf("LUN:%d found\n", list->luns[i]);
+		clnt->lun = list->luns[i];
+	}
+
+	printf("Will use LUN:%d\n", clnt->lun);
+	printf("Send testunitready to lun %d\n", clnt->lun);
+	if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
+		printf("failed to send testunitready command\n");
+		exit(10);
+	}
+}
+
+
+void normallogin_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	if (status != 0) {
+		printf("Failed to log in to target. status :0x%04x\n", status);
+		exit(10);
+	}
+
+	printf("Logged in normal session, send reportluns\n");
+	if (iscsi_reportluns_async(iscsi, reportluns_cb, 0, 16, private_data) != 0) {
+		printf("failed to send reportluns command\n");
+		exit(10);
+	}
+}
+
+
+void normalconnect_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	printf("Connected to iscsi socket\n");
+
+	if (status != 0) {
+		printf("normalconnect_cb: connection  failed status:%d\n", status);
+		exit(10);
+	}
+
+	printf("connected, send login command\n");
+	iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
+	if (iscsi_login_async(iscsi, normallogin_cb, private_data) != 0) {
+		printf("iscsi_login_async failed\n");
+		exit(10);
+	}
+}
+
+
+
+void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	
+	printf("discovery session logged out, Message from main() was:[%s]\n", clnt->message);
+
+	printf("disconnect socket\n");
+	if (iscsi_disconnect(iscsi) != 0) {
+		printf("Failed to disconnect old socket\n");
+		exit(10);
+	}
+
+	printf("reconnect with normal login to [%s]\n", clnt->target_address);
+	printf("Use targetname [%s] when connecting\n", clnt->target_name);
+	if (iscsi_set_targetname(iscsi, clnt->target_name)) {
+		printf("Failed to set target name\n");
+		exit(10);
+	}
+	if (iscsi_set_alias(iscsi, "ronnie") != 0) {
+		printf("Failed to add alias\n");
+		exit(10);
+	}
+	if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+		printf("Failed to set settion type to normal\n");
+		exit(10);
+	}
+
+	if (iscsi_connect_async(iscsi, clnt->target_address, normalconnect_cb, clnt) != 0) {
+		printf("iscsi_connect failed\n");
+		exit(10);
+	}
+}
+
+void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	struct client_state *clnt = (struct client_state *)private_data;
+	struct iscsi_discovery_address *addr;
+
+	printf("discovery callback   status:%04x\n", status);
+	for(addr=command_data; addr; addr=addr->next) {	
+		printf("Target:%s Address:%s\n", addr->target_name, addr->target_address);
+	}
+
+	addr=command_data;
+	clnt->has_discovered_target = 1;
+	clnt->target_name    = strdup(addr->target_name);
+	clnt->target_address = strdup(addr->target_address);
+
+
+	printf("discovery complete, send logout command\n");
+
+	if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) {
+		printf("iscsi_logout_async failed\n");
+		exit(10);
+	}
+}
+
+
+void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	if (status != 0) {
+		printf("Failed to log in to target. status :0x%04x\n", status);
+		exit(10);
+	}
+
+	printf("Logged in to target, send discovery command\n");
+	if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) {
+		printf("failed to send discovery command\n");
+		exit(10);
+	}
+
+}
+
+void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
+{
+	printf("Connected to iscsi socket status:0x%08x\n", status);
+
+	if (status != 0) {
+		printf("discoveryconnect_cb: connection  failed status:%d\n", status);
+		exit(10);
+	}
+
+	printf("connected, send login command\n");
+	iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY);
+	if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) {
+		printf("iscsi_login_async failed\n");
+		exit(10);
+	}
+}
+
+
+int main(int argc, char *argv[])
+{
+	struct iscsi_context *iscsi;
+	struct pollfd pfd;
+	struct client_state clnt;
+
+	printf("iscsi client\n");
+
+	iscsi = iscsi_create_context("iqn.2002-10.com.ronnie:client");
+	if (iscsi == NULL) {
+		printf("Failed to create context\n");
+		exit(10);
+	}
+
+	if (iscsi_set_alias(iscsi, "ronnie") != 0) {
+		printf("Failed to add alias\n");
+		exit(10);
+	}
+
+	clnt.message = "Hello iSCSI";
+	clnt.has_discovered_target = 0;
+	if (iscsi_connect_async(iscsi, TARGET, discoveryconnect_cb, &clnt) != 0) {
+		printf("iscsi_connect failed\n");
+		exit(10);
+	}
+
+	for (;;) {
+		pfd.fd = iscsi_get_fd(iscsi);
+		pfd.events = iscsi_which_events(iscsi);
+
+		if (poll(&pfd, 1, -1) < 0) {
+			printf("Poll failed");
+			exit(10);
+		}
+		if (iscsi_service(iscsi, pfd.revents) < 0) {
+			printf("iscsi_service failed\n");
+			break;
+		}
+	}
+
+printf("STOP\n");
+exit(10);
+
+	printf("ok\n");
+	return 0;
+}
+