dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / kernel / comstar / port / iscsit / iscsit_text.c
blobd2943a9f9d2c0868feb44390e6c2327946b06887
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27 #include <sys/cpuvar.h>
28 #include <sys/types.h>
29 #include <sys/conf.h>
30 #include <sys/file.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/modctl.h>
34 #include <sys/sysmacros.h>
35 #include <sys/socket.h>
36 #include <sys/strsubr.h>
37 #include <inet/tcp.h>
38 #include <sys/nvpair.h>
40 #include <sys/stmf.h>
41 #include <sys/stmf_ioctl.h>
42 #include <sys/portif.h>
43 #include <sys/idm/idm.h>
44 #include <sys/idm/idm_conn_sm.h>
45 #include <sys/idm/idm_text.h>
46 #include <sys/idm/idm_so.h>
48 #include "iscsit_isns.h"
49 #include "iscsit.h"
51 #define IPADDRSTRLEN INET6_ADDRSTRLEN /* space for ipaddr string */
52 #define PORTALSTRLEN (IPADDRSTRLEN+16) /* add space for :port,tag */
54 void
55 iscsit_text_cmd_fini(iscsit_conn_t *ict);
58 * The kernel inet_ntop() function formats ipv4 address fields with
59 * leading zeros which the win2k initiator interprets as octal.
62 static void
63 iscsit_v4_ntop(struct in_addr *in, char a[], int size)
65 unsigned char *p = (unsigned char *) in;
67 (void) snprintf(a, size, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3));
70 static void
71 iscsit_bump_ttt(iscsit_conn_t *ict)
74 * Set the target task tag. The value will be zero when
75 * the connection is created. Increment it and wrap it
76 * back to one if we hit the reserved value.
78 * The TTT is fabricated since there is no real task associated
79 * with a text request. The idm task range is reused here since
80 * no real tasks can be started from a discovery session and
81 * thus no conflicts are possible.
83 if (++ict->ict_text_rsp_ttt == IDM_TASKIDS_MAX)
84 ict->ict_text_rsp_ttt = 1;
87 static void
88 iscsit_text_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status)
90 iscsit_conn_t *ict = pdu->isp_private;
92 idm_pdu_free(pdu);
93 if (status != IDM_STATUS_SUCCESS) {
95 * Could not send the last text response.
96 * Clear any state and bump the TTT so subsequent
97 * requests will not match.
99 iscsit_text_cmd_fini(ict);
100 iscsit_bump_ttt(ict);
102 iscsit_conn_rele(ict);
105 static void
106 iscsit_text_reject(idm_pdu_t *req_pdu, uint8_t reason_code)
108 iscsit_conn_t *ict = req_pdu->isp_ic->ic_handle;
111 * A reject means abandoning this text request.
112 * Cleanup any state from the request and increment the TTT
113 * in case the initiator does not get the reject response
114 * and attempts to resume this request.
116 iscsit_text_cmd_fini(ict);
117 iscsit_bump_ttt(ict);
118 iscsit_send_reject(ict, req_pdu, reason_code);
119 idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS);
125 * Add individual <TargetAddress=ipaddr> tuple to the nvlist
127 static void
128 iscsit_add_portal(struct sockaddr_storage *ss, int tag, nvlist_t *nv_resp)
130 char ipaddr[IPADDRSTRLEN]; /* ip address string */
131 char ta_value[PORTALSTRLEN]; /* target address value */
132 struct sockaddr_in *sin;
133 struct sockaddr_in6 *sin6;
135 switch (ss->ss_family) {
136 case AF_INET:
137 sin = (struct sockaddr_in *)ss;
138 iscsit_v4_ntop(&sin->sin_addr, ipaddr, sizeof (ipaddr));
139 (void) snprintf(ta_value, sizeof (ta_value), "%s:%d,%d",
140 ipaddr, ntohs(sin->sin_port), tag);
141 break;
142 case AF_INET6:
143 sin6 = (struct sockaddr_in6 *)ss;
144 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, ipaddr,
145 sizeof (ipaddr));
146 (void) snprintf(ta_value, sizeof (ta_value), "[%s]:%d,%d",
147 ipaddr, ntohs(sin6->sin6_port), tag);
148 break;
149 default:
150 ASSERT(0);
151 return;
153 (void) nvlist_add_string(nv_resp, "TargetAddress", ta_value);
157 * Process the special case of the default portal group.
158 * Network addresses are obtained from the network stack and
159 * require some reformatting.
161 static void
162 iscsit_add_default_portals(iscsit_conn_t *ict, idm_addr_list_t *ipaddr_p,
163 nvlist_t *nv_resp)
165 int pass, i;
166 idm_addr_t *tip;
167 struct sockaddr_storage ss;
168 struct sockaddr_in *sin;
169 struct sockaddr_in6 *sin6;
172 * If this request was received on one of the portals,
173 * output that portal first. Most initiators will try to
174 * connect on the first portal in the SendTargets response.
175 * For example, this will avoid the confusing situation of a
176 * discovery coming in on an IB interface and the initiator
177 * then doing the normal login on an ethernet interface.
179 sin = (struct sockaddr_in *)&ss;
180 sin6 = (struct sockaddr_in6 *)&ss;
181 for (pass = 1; pass <= 2; pass++) {
182 tip = &ipaddr_p->al_addrs[0];
183 for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) {
184 /* Convert the address into sockaddr_storage format */
185 switch (tip->a_addr.i_insize) {
186 case sizeof (struct in_addr):
187 sin->sin_family = AF_INET;
188 sin->sin_port = htons(ISCSI_LISTEN_PORT);
189 sin->sin_addr = tip->a_addr.i_addr.in4;
190 break;
191 case sizeof (struct in6_addr):
192 sin6->sin6_family = AF_INET6;
193 sin6->sin6_port = htons(ISCSI_LISTEN_PORT);
194 sin6->sin6_addr = tip->a_addr.i_addr.in6;
195 break;
196 default:
197 ASSERT(0);
198 continue;
200 switch (pass) {
201 case 1:
203 * On the first pass, skip portals that
204 * do not match the incoming connection.
206 if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
207 B_TRUE, B_TRUE) != 0)
208 continue;
209 break;
210 case 2:
212 * On the second pass, process the
213 * remaining portals.
215 if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
216 B_TRUE, B_TRUE) == 0)
217 continue;
218 break;
221 * Add portal to the response list.
222 * By convention, the default portal group tag == 1
224 iscsit_add_portal(&ss, 1, nv_resp);
230 * Process a portal group from the configuration database.
232 static void
233 iscsit_add_portals(iscsit_conn_t *ict, iscsit_tpgt_t *tpg_list,
234 nvlist_t *nv_resp)
236 int pass;
237 iscsit_portal_t *portal, *next_portal;
238 iscsit_tpg_t *tpg;
239 struct sockaddr_storage *ss;
242 * As with the default portal group, output the portal used by
243 * the incoming request first.
245 tpg = tpg_list->tpgt_tpg;
246 for (pass = 1; pass <= 2; pass++) {
247 for (portal = avl_first(&tpg->tpg_portal_list);
248 portal != NULL;
249 portal = next_portal) {
251 next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
252 ss = &portal->portal_addr;
253 switch (pass) {
254 case 1:
256 * On the first pass, skip portals that
257 * do not match the incoming connection.
259 if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
260 B_TRUE, B_TRUE) != 0)
261 continue;
262 break;
263 case 2:
265 * On the second pass, process the
266 * remaining portals.
268 if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
269 B_TRUE, B_TRUE) == 0)
270 continue;
271 break;
273 /* Add portal to the response list */
274 iscsit_add_portal(ss, tpg_list->tpgt_tag, nv_resp);
280 * Process all the portal groups bound to a particular target.
282 static void
283 iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target,
284 idm_addr_list_t *ipaddr_p, nvlist_t *nv_resp)
286 iscsit_tpgt_t *tpg_list;
289 * Look through the portal groups associated with this target.
291 mutex_enter(&target->target_mutex);
292 tpg_list = avl_first(&target->target_tpgt_list);
294 /* check for the default portal group */
295 if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) {
297 * The default portal group is a special case and will
298 * return all reasonable interfaces on this node.
300 * A target cannot be bound to other portal groups
301 * if it is bound to the default portal group.
303 ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL);
305 if (ipaddr_p != NULL) {
306 /* convert the ip address list to nvlist format */
307 iscsit_add_default_portals(ict, ipaddr_p, nv_resp);
309 mutex_exit(&target->target_mutex);
310 return;
314 * Not the default portal group - process the user defined tpgs
316 ASSERT(tpg_list != NULL);
317 while (tpg_list != NULL) {
319 ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg);
322 * Found a defined portal group - add each portal address.
323 * As with the default portal group, make 2 passes over
324 * the addresses in order to output the connection
325 * address first.
327 iscsit_add_portals(ict, tpg_list, nv_resp);
329 tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list);
331 mutex_exit(&target->target_mutex);
334 #ifdef DEBUG
336 * To test with smaller PDUs in order to force multi-PDU responses,
337 * set this value such that: 0 < test_max_len < 8192
339 uint32_t iscsit_text_max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
340 #endif
343 * Format a text response PDU from the text buffer and send it.
345 static void
346 iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
348 iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
349 iscsi_text_rsp_hdr_t *th_resp;
350 idm_pdu_t *resp;
351 uint32_t len, remainder, max_len;
352 char *base;
353 boolean_t final;
355 max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
356 #ifdef DEBUG
357 if (iscsit_text_max_len > 0 && iscsit_text_max_len < max_len)
358 max_len = iscsit_text_max_len;
359 #endif
360 do {
361 remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off;
362 if (remainder <= max_len) {
363 len = remainder;
364 final = B_TRUE;
365 } else {
366 len = max_len;
367 final = B_FALSE;
370 * Allocate a PDU and copy in text response buffer
372 resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len);
373 idm_pdu_init(resp, ict->ict_ic, ict,
374 iscsit_text_resp_complete_cb);
375 /* Advance the StatSN for each Text Response sent */
376 resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
377 base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off;
378 bcopy(base, resp->isp_data, len);
380 * Fill in the response header
382 th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr;
383 bzero(th_resp, sizeof (*th_resp));
384 th_resp->opcode = ISCSI_OP_TEXT_RSP;
385 th_resp->itt = th_req->itt;
386 hton24(th_resp->dlength, len);
387 if (final) {
388 th_resp->flags = ISCSI_FLAG_FINAL;
389 th_resp->ttt = ISCSI_RSVD_TASK_TAG;
390 kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
391 ict->ict_text_rsp_buf = NULL;
392 ict->ict_text_rsp_len = 0;
393 ict->ict_text_rsp_valid_len = 0;
394 ict->ict_text_rsp_off = 0;
395 } else {
396 th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE;
397 th_resp->ttt = ict->ict_text_rsp_ttt;
398 ict->ict_text_rsp_off += len;
400 /* Send the response on its way */
401 iscsit_conn_hold(ict);
402 iscsit_pdu_tx(resp);
403 } while (!final);
404 /* Free the request pdu */
405 idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
409 * Clean-up the text buffer if it exists.
411 void
412 iscsit_text_cmd_fini(iscsit_conn_t *ict)
414 if (ict->ict_text_rsp_buf != NULL) {
415 ASSERT(ict->ict_text_rsp_len != 0);
416 kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
418 ict->ict_text_rsp_buf = NULL;
419 ict->ict_text_rsp_len = 0;
420 ict->ict_text_rsp_valid_len = 0;
421 ict->ict_text_rsp_off = 0;
425 * Process an iSCSI text command.
427 * This code only handles the common case of a text command
428 * containing the single tuple SendTargets=All issued during
429 * a discovery session. The request will always arrive in a
430 * single PDU, but the response may span multiple PDUs if the
431 * configuration is large. I.e. many targets and portals.
433 * The request is checked for correctness and then the response
434 * is generated from the global target into nvlist format. Then
435 * the nvlist is reformatted into idm textbuf format which reflects
436 * the iSCSI defined <name=value> specification. Finally, the
437 * textbuf is sent to the initiator in one or more text response PDUs
439 void
440 iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
442 iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
443 nvlist_t *nv_resp;
444 char *kv_pair;
445 int flags;
446 char *textbuf;
447 int textbuflen;
448 int validlen;
449 int rc;
451 flags = th_req->flags;
452 if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) {
453 /* Cannot handle multi-PDU requests now */
454 iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
455 return;
457 if (th_req->ttt != ISCSI_RSVD_TASK_TAG) {
459 * This is the initiator acknowledging our last PDU and
460 * indicating it is ready for the next PDU in the sequence.
463 * There can only be one outstanding text request on a
464 * connection. Make sure this one PDU has the current TTT.
466 /* XXX combine the following 3 checks after testing */
467 if (th_req->ttt != ict->ict_text_rsp_ttt) {
468 /* Not part of this sequence */
469 iscsit_text_reject(rx_pdu,
470 ISCSI_REJECT_CMD_NOT_SUPPORTED);
471 return;
474 * ITT should match what was saved from first PDU.
476 if (th_req->itt != ict->ict_text_req_itt) {
477 /* Not part of this sequence */
478 iscsit_text_reject(rx_pdu,
479 ISCSI_REJECT_CMD_NOT_SUPPORTED);
480 return;
483 * Cannot deal with more key/value pairs now.
485 if (rx_pdu->isp_datalen != 0) {
486 iscsit_text_reject(rx_pdu,
487 ISCSI_REJECT_CMD_NOT_SUPPORTED);
488 return;
490 iscsit_send_next_text_response(ict, rx_pdu);
491 return;
495 * Initiator has started a new text request. Only
496 * one can be active at a time, so abandon any previous
497 * text request on this connection.
499 iscsit_text_cmd_fini(ict);
501 /* Set the target task tag. */
502 iscsit_bump_ttt(ict);
504 /* Save the initiator task tag */
505 ict->ict_text_req_itt = th_req->itt;
508 * Make sure this is a proper SendTargets request
510 textbuf = (char *)rx_pdu->isp_data;
511 textbuflen = rx_pdu->isp_datalen;
512 kv_pair = "SendTargets=All";
513 if (textbuflen >= strlen(kv_pair) &&
514 strcmp(kv_pair, textbuf) == 0 &&
515 ict->ict_op.op_discovery_session == B_TRUE) {
517 * Most common case of SendTargets=All during discovery.
519 idm_addr_list_t *ipaddr_p;
520 iscsit_tgt_t *tgt, *ntgt;
521 int ipsize;
524 /* Create an nvlist for response */
525 if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) {
526 iscsit_text_reject(rx_pdu,
527 ISCSI_REJECT_CMD_NOT_SUPPORTED);
528 return;
531 /* Get the list of local interface addresses */
532 ipsize = idm_get_ipaddr(&ipaddr_p);
534 /* Add targets to the response list */
535 ISCSIT_GLOBAL_LOCK(RW_READER);
536 for (tgt = avl_first(&iscsit_global.global_target_list);
537 tgt != NULL; tgt = ntgt) {
538 struct sockaddr_storage v4sa, *sa;
539 iscsit_tgt_state_t state;
540 iscsit_portal_t *portal;
541 iscsit_tpgt_t *tpgt;
543 ntgt = AVL_NEXT(&iscsit_global.global_target_list, tgt);
545 /* Only report online and onlining targets */
546 state = tgt->target_state;
547 if (state != TS_ONLINING && state != TS_ONLINE &&
548 state != TS_STMF_ONLINE)
549 continue;
552 * Report target if:
553 * - it is bound to default TPG
554 * - one of the addresses of TPGs the target is bound
555 * to matches incoming connection dst address
557 sa = &ict->ict_ic->ic_laddr;
558 mutex_enter(&tgt->target_mutex);
559 tpgt = avl_first(&tgt->target_tpgt_list);
560 if (!(IS_DEFAULT_TPGT(tpgt))) {
561 portal = iscsit_tgt_lookup_portal(tgt, sa,
562 &tpgt);
563 if (portal == NULL &&
564 iscsit_is_v4_mapped(sa, &v4sa)) {
565 portal = iscsit_tgt_lookup_portal(tgt,
566 &v4sa, &tpgt);
568 if (portal == NULL) {
569 mutex_exit(&tgt->target_mutex);
570 continue;
572 iscsit_portal_rele(portal);
573 iscsit_tpgt_rele(tpgt);
575 mutex_exit(&tgt->target_mutex);
577 if (nvlist_add_string(nv_resp, "TargetName",
578 tgt->target_name) == 0) {
579 /* Add the portal groups bound to this target */
580 iscsit_add_tpgs(ict, tgt, ipaddr_p, nv_resp);
583 ISCSIT_GLOBAL_UNLOCK();
584 if (ipsize > 0)
585 kmem_free(ipaddr_p, ipsize);
587 /* Convert the response nvlist into an idm text buffer */
588 textbuf = 0;
589 textbuflen = 0;
590 validlen = 0;
591 rc = idm_nvlist_to_textbuf(nv_resp, &textbuf,
592 &textbuflen, &validlen);
593 nvlist_free(nv_resp);
594 if (rc != 0) {
595 if (textbuf && textbuflen)
596 kmem_free(textbuf, textbuflen);
597 iscsit_text_reject(rx_pdu,
598 ISCSI_REJECT_CMD_NOT_SUPPORTED);
599 return;
601 ict->ict_text_rsp_buf = textbuf;
602 ict->ict_text_rsp_len = textbuflen;
603 ict->ict_text_rsp_valid_len = validlen;
604 ict->ict_text_rsp_off = 0;
605 iscsit_send_next_text_response(ict, rx_pdu);
606 } else {
608 * Other cases to handle
609 * Discovery session:
610 * SendTargets=<target_name>
611 * Normal session
612 * SendTargets=<NULL> - assume target name of session
613 * All others
614 * Error
616 iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
617 return;