configurator: HAVE_SECTION_START_STOP
[ccan] / ccan / iscsi / pdu.c
1 /*
2    Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <strings.h>
21 #include <string.h>
22 #include <arpa/inet.h>
23 #include "iscsi.h"
24 #include "iscsi-private.h"
25 #include "scsi-lowlevel.h"
26 #include "dlinklist.h"
27
28 struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode)
29 {
30         struct iscsi_pdu *pdu;
31
32         pdu = malloc(sizeof(struct iscsi_pdu));
33         if (pdu == NULL) {
34                 printf("failed to allocate pdu\n");
35                 return NULL;
36         }
37         bzero(pdu, sizeof(struct iscsi_pdu));
38
39         pdu->outdata.size = ISCSI_HEADER_SIZE;
40         pdu->outdata.data = malloc(pdu->outdata.size);
41
42         if (pdu->outdata.data == NULL) {
43                 printf("failed to allocate pdu header\n");
44                 free(pdu);
45                 pdu = NULL;
46                 return NULL;
47         }
48         bzero(pdu->outdata.data, pdu->outdata.size);
49
50         /* opcode */
51         pdu->outdata.data[0] = opcode;
52         pdu->response_opcode = response_opcode;
53
54         /* isid */
55         if (opcode ==ISCSI_PDU_LOGIN_REQUEST) {
56                 memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
57         }
58
59         /* itt */
60         *(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
61         pdu->itt = iscsi->itt;
62
63         iscsi->itt++;
64
65         return pdu;
66 }
67
68 void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
69 {
70         if (pdu == NULL) {
71                 printf("trying to free NULL pdu\n");
72                 return;
73         }
74         if (pdu->outdata.data) {
75                 free(pdu->outdata.data);
76                 pdu->outdata.data = NULL;
77         }
78         if (pdu->indata.data) {
79                 free(pdu->indata.data);
80                 pdu->indata.data = NULL;
81         }
82         if (pdu->scsi_cbdata) {
83                 iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
84                 pdu->scsi_cbdata = NULL;
85         }
86
87         free(pdu);
88 }
89
90
91 int iscsi_add_data(struct iscsi_data *data, const unsigned char *dptr, int dsize, int pdualignment)
92 {
93         int len, aligned;
94         unsigned char *buf;
95
96         if (dsize == 0) {
97                 printf("Trying to append zero size data to iscsi_data\n");
98                 return -1;
99         }
100
101         len = data->size + dsize;
102         aligned = len;
103         if (pdualignment) {
104                 aligned = (aligned+3)&0xfffffffc;
105         }
106         buf = malloc(aligned);
107         if (buf == NULL) {
108                 printf("failed to allocate buffer for %d bytes\n", len);
109                 return -2;
110         }
111
112         memcpy(buf, data->data, data->size);
113         memcpy(buf + data->size, dptr, dsize);
114         if (len != aligned) {
115                 /* zero out any padding at the end */
116                bzero(buf+len, aligned-len);
117         }
118
119         free(data->data);
120         data->data  = buf;
121         data->size = len;
122
123         return 0;
124 }
125
126 int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *dptr, int dsize)
127 {
128         if (pdu == NULL) {
129                 printf("trying to add data to NULL pdu\n");
130                 return -1;
131         }
132         if (dsize == 0) {
133                 printf("Trying to append zero size data to pdu\n");
134                 return -2;
135         }
136
137         if (iscsi_add_data(&pdu->outdata, dptr, dsize, 1) != 0) {
138                 printf("failed to add data to pdu buffer\n");
139                 return -3;
140         }
141
142         /* update data segment length */
143         *(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size-ISCSI_HEADER_SIZE);
144
145         return 0;
146 }
147
148 int iscsi_get_pdu_size(const unsigned char *hdr)
149 {
150         int size;
151
152         size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
153         size = (size+3)&0xfffffffc;
154
155         return size;
156 }
157
158
159 int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size)
160 {
161         uint32_t itt;
162         enum iscsi_opcode opcode;
163         struct iscsi_pdu *pdu;
164         uint8_t ahslen;
165
166         opcode = hdr[0] & 0x3f;
167         ahslen = hdr[4];
168         itt = ntohl(*(uint32_t *)&hdr[16]);
169
170         if (ahslen != 0) {
171                 printf("cant handle expanded headers yet\n");
172                 return -1;
173         }
174
175         for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
176                 enum iscsi_opcode expected_response = pdu->response_opcode;
177                 int is_finished = 1;
178
179                 if (pdu->itt != itt) {
180                         continue;
181                 }
182
183                 /* we have a special case with scsi-command opcodes, the are replied to by either a scsi-response
184                  * or a data-in, or a combination of both.
185                  */
186                 if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) {
187                         expected_response = ISCSI_PDU_DATA_IN;
188                 }
189                                 
190                 if (opcode != expected_response) {
191                         printf("Got wrong opcode back for itt:%d  got:%d expected %d\n", itt, opcode, pdu->response_opcode);
192                         return -1;
193                 }
194                 switch (opcode) {
195                 case ISCSI_PDU_LOGIN_RESPONSE:
196                         if (iscsi_process_login_reply(iscsi, pdu, hdr, size) != 0) {
197                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
198                                 iscsi_free_pdu(iscsi, pdu);
199                                 printf("iscsi login reply failed\n");
200                                 return -2;
201                         }
202                         break;
203                 case ISCSI_PDU_TEXT_RESPONSE:
204                         if (iscsi_process_text_reply(iscsi, pdu, hdr, size) != 0) {
205                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
206                                 iscsi_free_pdu(iscsi, pdu);
207                                 printf("iscsi text reply failed\n");
208                                 return -2;
209                         }
210                         break;
211                 case ISCSI_PDU_LOGOUT_RESPONSE:
212                         if (iscsi_process_logout_reply(iscsi, pdu, hdr, size) != 0) {
213                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
214                                 iscsi_free_pdu(iscsi, pdu);
215                                 printf("iscsi logout reply failed\n");
216                                 return -3;
217                         }
218                         break;
219                 case ISCSI_PDU_SCSI_RESPONSE:
220                         if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size) != 0) {
221                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
222                                 iscsi_free_pdu(iscsi, pdu);
223                                 printf("iscsi response reply failed\n");
224                                 return -4;
225                         }
226                         break;
227                 case ISCSI_PDU_DATA_IN:
228                         if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size, &is_finished) != 0) {
229                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
230                                 iscsi_free_pdu(iscsi, pdu);
231                                 printf("iscsi data in failed\n");
232                                 return -4;
233                         }
234                         break;
235                 case ISCSI_PDU_NOP_IN:
236                         if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size) != 0) {
237                                 DLIST_REMOVE(iscsi->waitpdu, pdu);
238                                 iscsi_free_pdu(iscsi, pdu);
239                                 printf("iscsi nop-in failed\n");
240                                 return -5;
241                         }
242                         break;
243                 default:
244                         printf("Don't know how to handle opcode %d\n", opcode);
245                         return -2;
246                 }
247
248                 if (is_finished) {
249                         DLIST_REMOVE(iscsi->waitpdu, pdu);
250                         iscsi_free_pdu(iscsi, pdu);
251                 } else {
252                         printf("pdu is not yet finished, let it remain\n");
253                 }
254                 return 0;
255         }
256
257         return 0;
258 }
259
260 void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
261 {
262         pdu->outdata.data[1] = flags;
263 }
264
265 void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
266 {
267         pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
268 }
269
270 void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
271 {
272         *(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
273 }
274
275 void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
276 {
277         *(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
278 }
279
280 void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
281 {
282         *(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
283 }
284
285 void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
286 {
287         bzero(&pdu->outdata.data[32], 16);
288         memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
289 }
290
291 void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
292 {
293         pdu->outdata.data[9] = lun;
294 }
295
296 void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
297 {
298         *(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
299 }