Move init.h to gpxe/init.h.
[people/xl0/gpxe.git] / src / proto / iscsi.c
1 #include <stddef.h>
2 #include <string.h>
3 #include <vsprintf.h>
4 #include <assert.h>
5 #include <byteswap.h>
6 #include <gpxe/iscsi.h>
7
8 /** @file
9  *
10  * iSCSI protocol
11  *
12  */
13
14 /****************************************************************************
15  *
16  * Utility functions
17  *
18  */
19
20 /**
21  * Start up a new TX PDU
22  *
23  * @v iscsi             iSCSI session
24  *
25  * This initiates the process of sending a new PDU.  Only one PDU may
26  * be in transit at any one time.
27  */
28 static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
29         assert ( iscsi->tx_state == ISCSI_TX_IDLE );
30         iscsi->tx_state = ISCSI_TX_BHS;
31         iscsi->tx_offset = 0;
32 }
33
34 /**
35  * Mark session as failed
36  *
37  * @v iscsi             iSCSI session
38  *
39  * This marks the session as permanently failed.  The session will not
40  * be automatically logged back in.
41  */
42 static void iscsi_fail ( struct iscsi_session *iscsi ) {
43         iscsi->state = ISCSI_STATE_FAILED;
44         tcp_close ( &iscsi->tcp );
45 }
46
47 /****************************************************************************
48  *
49  * iSCSI SCSI command issuing
50  *
51  */
52
53 /**
54  * Start up a block read
55  *
56  * @v iscsi             iSCSI session
57  *
58  */
59 static void iscsi_start_read_block ( struct iscsi_session *iscsi ) {
60         struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
61         struct scsi_cdb_read_10 *read = &command->cdb.read_10;
62
63         assert ( iscsi->block_size != 0 );
64         assert ( iscsi->block_count != 0 );
65         assert ( iscsi->block_read_callback != NULL );
66
67         /* Construct BHS */
68         memset ( command, 0, sizeof ( *command ) );
69         command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
70         command->flags = ( ISCSI_FLAG_FINAL |
71                            ISCSI_COMMAND_FLAG_READ |
72                            ISCSI_COMMAND_ATTR_SIMPLE );
73         /* lengths left as zero */
74         /* lun left as zero, on the assumption that no-one uses LUNs > 0 */
75         command->itt = htonl ( iscsi->itt );
76         command->exp_len = htonl ( iscsi->block_count * iscsi->block_size );
77         command->cmdsn = htonl ( iscsi->cmdsn );
78         command->expstatsn = htonl ( iscsi->statsn + 1 );
79         read->opcode = SCSI_OPCODE_READ_10;
80         read->lba = htonl ( iscsi->block_start );
81         read->len = htons ( iscsi->block_count );
82
83         iscsi->state = ISCSI_STATE_READING_DATA;
84         iscsi_start_tx ( iscsi );
85 }
86
87 /**
88  * Receive data segment of an iSCSI data-in PDU
89  *
90  * @v iscsi             iSCSI session
91  * @v data              Received data
92  * @v len               Length of received data
93  * @v remaining         Data remaining after this data
94  * 
95  */
96 static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
97                                size_t len, size_t remaining ) {
98         struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
99         unsigned long offset;
100
101         /* Update cmdsn and statsn */
102         iscsi->cmdsn = ntohl ( data_in->expcmdsn );
103         iscsi->statsn = ntohl ( data_in->statsn );
104
105         /* Process data via callback */
106         offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
107         iscsi->block_read_callback ( iscsi->block_read_private,
108                                      data, offset, len );
109
110         /* If this is the end, mark state as idle */
111         if ( ( data_in->flags & ISCSI_FLAG_FINAL ) && ( remaining == 0 ) )
112                 iscsi->state = ISCSI_STATE_IDLE;
113 }
114
115 /****************************************************************************
116  *
117  * iSCSI login
118  *
119  */
120
121 /**
122  * Build iSCSI login request strings
123  *
124  * @v iscsi             iSCSI session
125  *
126  * These are the initial set of strings sent in the first login
127  * request PDU.
128  */
129 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
130                                                void *data, size_t len ) {
131         return snprintf ( data, len,
132                           "InitiatorName=%s:initiator%c"
133                           "TargetName=%s%c"
134                           "MaxRecvDataSegmentLength=512%c"
135                           "SessionType=Normal%c"
136                           "DataDigest=None%c"
137                           "HeaderDigest=None%c",
138                           iscsi->initiator, 0, iscsi->target, 0,
139                           0, 0, 0, 0 );
140 }
141
142 /**
143  * Transmit data segment of an iSCSI login request PDU
144  *
145  * @v iscsi             iSCSI session
146  *
147  * For login requests, the data segment consists of the login strings.
148  */
149 static void iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
150         int len;
151
152         len = iscsi_build_login_request_strings ( iscsi, tcp_buffer,
153                                                   tcp_buflen );
154         tcp_send ( &iscsi->tcp, tcp_buffer + iscsi->tx_offset,
155                    len - iscsi->tx_offset );
156 }
157
158 /**
159  * Start up a login request
160  *
161  * @v iscsi             iSCSI session
162  *
163  */
164 static void iscsi_start_login ( struct iscsi_session *iscsi ) {
165         struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
166         int len;
167
168         /* Construct login request BHS */
169         memset ( request, 0, sizeof ( *request ) );
170         request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
171                             ISCSI_FLAG_IMMEDIATE );
172         request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION |
173                            ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |
174                            ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE );
175         /* version_max and version_min left as zero */
176         len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
177         ISCSI_SET_LENGTHS ( request->lengths, 0, len );
178         request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
179                                         IANA_EN_FEN_SYSTEMS );
180         /* isid_iana_qual left as zero */
181         request->tsih = htons ( iscsi->tsih );
182         /* itt left as zero */
183         /* cid left as zero */
184         request->cmdsn = htonl ( iscsi->cmdsn );
185         request->expstatsn = htonl ( iscsi->statsn + 1 );
186
187         iscsi->state = ISCSI_STATE_LOGGING_IN;
188         iscsi_start_tx ( iscsi );
189 }
190
191 /**
192  * Receive data segment of an iSCSI login response PDU
193  *
194  * @v iscsi             iSCSI session
195  * @v data              Received data
196  * @v len               Length of received data
197  * @v remaining         Data remaining after this data
198  * 
199  */
200 static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
201                                       void *data __unused,
202                                       size_t len __unused,
203                                       size_t remaining __unused ) {
204         struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
205         struct iscsi_bhs_login_response *response
206                 = &iscsi->rx_bhs.login_response;
207
208         /* Sanity check */
209         if ( iscsi->state != ISCSI_STATE_LOGGING_IN ) {
210                 printf ( "Spurious iSCSI login response\n" );
211                 iscsi_fail ( iscsi );
212                 return;
213         }
214
215         /* Check for fatal errors */
216         if ( response->status_class != 0 ) {
217                 printf ( "iSCSI login failure: class %02x detail %02x\n",
218                          response->status_class, response->status_detail );
219                 iscsi_fail ( iscsi );
220                 return;
221         }
222
223         /* Update cmdsn and statsn */
224         iscsi->cmdsn = ntohl ( response->expcmdsn );
225         iscsi->statsn = ntohl ( response->statsn );
226
227         /* If server did not transition, we send it another login
228          * request with empty strings.
229          */
230         if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
231                 ISCSI_SET_LENGTHS ( request->lengths, 0, 0 );
232                 iscsi_start_tx ( iscsi );
233                 return;
234         }
235
236         /* Record TSIH  for future reference */
237         iscsi->tsih = ntohl ( response->tsih );
238
239         /* Start reading data */
240         iscsi_start_read_block ( iscsi );
241 }
242
243 /****************************************************************************
244  *
245  * iSCSI to TCP interface
246  *
247  */
248
249 static inline struct iscsi_session *
250 tcp_to_iscsi ( struct tcp_connection *conn ) {
251         return container_of ( conn, struct iscsi_session, tcp );
252 }
253
254 static void iscsi_aborted ( struct tcp_connection *conn ) {
255         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
256
257 }
258
259 static void iscsi_timedout ( struct tcp_connection *conn ) {
260         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
261
262 }
263
264 static void iscsi_closed ( struct tcp_connection *conn ) {
265         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
266
267 }
268
269 static void iscsi_connected ( struct tcp_connection *conn ) {
270         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
271
272         /* Prepare to receive PDUs. */
273         iscsi->rx_state = ISCSI_RX_BHS;
274         iscsi->rx_offset = 0;
275
276         /* TX state should already have been set up */
277         assert ( iscsi->tx_state != ISCSI_TX_IDLE );
278         assert ( iscsi->tx_offset == 0 );
279 }
280
281 /**
282  * Transmit data segment of an iSCSI PDU
283  *
284  * @v iscsi             iSCSI session
285  * 
286  * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
287  * will be valid when this is called.
288  */
289 static void iscsi_tx_data ( struct iscsi_session *iscsi ) {
290         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
291
292         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
293         case ISCSI_OPCODE_LOGIN_REQUEST:
294                 iscsi_tx_login_request ( iscsi );
295                 break;
296         default:
297                 assert ( 0 );
298                 break;
299         }
300 }
301
302 /**
303  * Handle TCP ACKs
304  *
305  * @v iscsi             iSCSI session
306  * 
307  * Updates iscsi->tx_offset and, if applicable, transitions to the
308  * next TX state.
309  */
310 static void iscsi_acked ( struct tcp_connection *conn, size_t len ) {
311         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
312         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
313         size_t max_tx_offset;
314         enum iscsi_tx_state next_state;
315         
316         iscsi->tx_offset += len;
317         while ( 1 ) {
318                 switch ( iscsi->tx_state ) {
319                 case ISCSI_TX_BHS:
320                         max_tx_offset = sizeof ( iscsi->tx_bhs );
321                         next_state = ISCSI_TX_AHS;
322                         break;
323                 case ISCSI_TX_AHS:
324                         max_tx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
325                         next_state = ISCSI_TX_DATA;
326                         break;
327                 case ISCSI_TX_DATA:
328                         max_tx_offset = ISCSI_DATA_LEN ( common->lengths );
329                         next_state = ISCSI_TX_DATA_PADDING;
330                         break;
331                 case ISCSI_TX_DATA_PADDING:
332                         max_tx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
333                         next_state = ISCSI_TX_IDLE;
334                         break;
335                 case ISCSI_TX_IDLE:
336                         return;
337                 default:
338                         assert ( 0 );
339                         return;
340                 }
341                 assert ( iscsi->tx_offset <= max_tx_offset );
342
343                 /* If the whole of the current portion has not yet
344                  * been acked, stay in this state for now.
345                  */
346                 if ( iscsi->tx_offset != max_tx_offset )
347                         return;
348                 
349                 iscsi->tx_state = next_state;
350                 iscsi->tx_offset = 0;
351         }
352 }
353
354 /**
355  * Transmit iSCSI PDU
356  *
357  * @v iscsi             iSCSI session
358  * 
359  * Constructs data to be sent for the current TX state
360  */
361 static void iscsi_senddata ( struct tcp_connection *conn ) {
362         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
363         struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
364         static const char pad[] = { '\0', '\0', '\0' };
365
366         switch ( iscsi->tx_state ) {
367         case ISCSI_TX_IDLE:
368                 /* Do nothing */
369                 break;
370         case ISCSI_TX_BHS:
371                 tcp_send ( conn, &iscsi->tx_bhs.bytes[iscsi->tx_offset],
372                            ( sizeof ( iscsi->tx_bhs ) - iscsi->tx_offset ) );
373                 break;
374         case ISCSI_TX_AHS:
375                 /* We don't yet have an AHS transmission mechanism */
376                 assert ( 0 );
377                 break;
378         case ISCSI_TX_DATA:
379                 iscsi_tx_data ( iscsi );
380                 break;
381         case ISCSI_TX_DATA_PADDING:
382                 tcp_send ( conn, pad, ( ISCSI_DATA_PAD_LEN ( common->lengths )
383                                         - iscsi->tx_offset ) );
384                 break;
385         default:
386                 assert ( 0 );
387                 break;
388         }
389 }
390
391 /**
392  * Receive data segment of an iSCSI PDU
393  *
394  * @v iscsi             iSCSI session
395  * @v data              Received data
396  * @v len               Length of received data
397  * @v remaining         Data remaining after this data
398  *
399  * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
400  * will be valid when this is called.
401  */
402 static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
403                             size_t len, size_t remaining ) {
404         struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
405
406         switch ( common->opcode & ISCSI_OPCODE_MASK ) {
407         case ISCSI_OPCODE_LOGIN_RESPONSE:
408                 iscsi_rx_login_response ( iscsi, data, len, remaining );
409                 break;
410         case ISCSI_OPCODE_DATA_IN:
411                 iscsi_rx_data_in ( iscsi, data, len, remaining );
412                 break;
413         default:
414                 printf ( "Unknown iSCSI opcode %02x\n", common->opcode );
415                 break;
416         }
417 }
418
419 /**
420  * Discard portion of an iSCSI PDU.
421  *
422  * @v iscsi             iSCSI session
423  * @v data              Received data
424  * @v len               Length of received data
425  * @v remaining         Data remaining after this data
426  *
427  * This discards data from a portion of a received PDU.
428  */
429 static void iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
430                                void *data __unused, size_t len __unused,
431                                size_t remaining __unused ) {
432         /* Do nothing */
433 }
434
435 /**
436  * Receive basic header segment of an iSCSI PDU
437  *
438  * @v iscsi             iSCSI session
439  * @v data              Received data
440  * @v len               Length of received data
441  * @v remaining         Data remaining after this data
442  *
443  * This fills in iscsi::rx_bhs with the data from the BHS portion of
444  * the received PDU.
445  */
446 static void iscsi_rx_bhs ( struct iscsi_session *iscsi, void *data,
447                            size_t len, size_t remaining __unused ) {
448         memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
449 }
450
451 /**
452  * Receive new data
453  *
454  * @v tcp               TCP connection
455  * @v data              Received data
456  * @v len               Length of received data
457  *
458  * This handles received PDUs.  The receive strategy is to fill in
459  * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
460  * throw away any AHS portion, and then process each part of the data
461  * portion as it arrives.  The data processing routine therefore
462  * always has a full copy of the BHS available, even for portions of
463  * the data in different packets to the BHS.
464  */
465 static void iscsi_newdata ( struct tcp_connection *conn, void *data,
466                             size_t len ) {
467         struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
468         struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
469         void ( *process ) ( struct iscsi_session *iscsi, void *data,
470                             size_t len, size_t remaining );
471         size_t max_rx_offset;
472         enum iscsi_rx_state next_state;
473         size_t frag_len;
474         size_t remaining;
475
476         while ( 1 ) {
477                 switch ( iscsi->rx_state ) {
478                 case ISCSI_RX_BHS:
479                         process = iscsi_rx_bhs;
480                         max_rx_offset = sizeof ( iscsi->rx_bhs );
481                         next_state = ISCSI_RX_AHS;                      
482                         break;
483                 case ISCSI_RX_AHS:
484                         process = iscsi_rx_discard;
485                         max_rx_offset = 4 * ISCSI_AHS_LEN ( common->lengths );
486                         next_state = ISCSI_RX_DATA;
487                         break;
488                 case ISCSI_RX_DATA:
489                         process = iscsi_rx_data;
490                         max_rx_offset = ISCSI_DATA_LEN ( common->lengths );
491                         next_state = ISCSI_RX_DATA_PADDING;
492                         break;
493                 case ISCSI_RX_DATA_PADDING:
494                         process = iscsi_rx_discard;
495                         max_rx_offset = ISCSI_DATA_PAD_LEN ( common->lengths );
496                         next_state = ISCSI_RX_BHS;
497                         break;
498                 default:
499                         assert ( 0 );
500                         return;
501                 }
502
503                 frag_len = max_rx_offset - iscsi->rx_offset;
504                 if ( frag_len > len )
505                         frag_len = len;
506                 remaining = max_rx_offset - iscsi->rx_offset - frag_len;
507                 process ( iscsi, data, frag_len, remaining );
508
509                 iscsi->rx_offset += frag_len;
510                 data += frag_len;
511                 len -= frag_len;
512
513                 /* If all the data for this state has not yet been
514                  * received, stay in this state for now.
515                  */
516                 if ( iscsi->rx_offset != max_rx_offset )
517                         return;
518
519                 iscsi->rx_state = next_state;
520                 iscsi->rx_offset = 0;
521         }
522 }
523
524 /** iSCSI TCP operations */
525 static struct tcp_operations iscsi_tcp_operations = {
526         .aborted        = iscsi_aborted,
527         .timedout       = iscsi_timedout,
528         .closed         = iscsi_closed,
529         .connected      = iscsi_connected,
530         .acked          = iscsi_acked,
531         .newdata        = iscsi_newdata,
532         .senddata       = iscsi_senddata,
533 };
534
535 /**
536  * Wake up session
537  *
538  * @v iscsi             iSCSI session
539  *
540  */
541 void iscsi_wakeup ( struct iscsi_session *iscsi ) {
542         iscsi->tcp.tcp_op = &iscsi_tcp_operations;
543
544         switch ( iscsi->state ) {
545         case ISCSI_STATE_NOT_CONNECTED:
546         case ISCSI_STATE_FAILED:
547                 if ( tcp_connect ( &iscsi->tcp ) != 0 )
548                         iscsi_fail ( iscsi );
549                 iscsi_start_login ( iscsi );
550                 break;
551         case ISCSI_STATE_IDLE:
552                 iscsi_start_read_block ( iscsi );
553                 break;
554         default:
555                 /* Stay in same state */
556                 break;
557         }
558 }