1a6d42e7dSPeter Dunlap /*
2a6d42e7dSPeter Dunlap * CDDL HEADER START
3a6d42e7dSPeter Dunlap *
4a6d42e7dSPeter Dunlap * The contents of this file are subject to the terms of the
5a6d42e7dSPeter Dunlap * Common Development and Distribution License (the "License").
6a6d42e7dSPeter Dunlap * You may not use this file except in compliance with the License.
7a6d42e7dSPeter Dunlap *
8a6d42e7dSPeter Dunlap * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a6d42e7dSPeter Dunlap * or http://www.opensolaris.org/os/licensing.
10a6d42e7dSPeter Dunlap * See the License for the specific language governing permissions
11a6d42e7dSPeter Dunlap * and limitations under the License.
12a6d42e7dSPeter Dunlap *
13a6d42e7dSPeter Dunlap * When distributing Covered Code, include this CDDL HEADER in each
14a6d42e7dSPeter Dunlap * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a6d42e7dSPeter Dunlap * If applicable, add the following below this CDDL HEADER, with the
16a6d42e7dSPeter Dunlap * fields enclosed by brackets "[]" replaced with your own identifying
17a6d42e7dSPeter Dunlap * information: Portions Copyright [yyyy] [name of copyright owner]
18a6d42e7dSPeter Dunlap *
19a6d42e7dSPeter Dunlap * CDDL HEADER END
20a6d42e7dSPeter Dunlap */
21483b029bSYuri Pankov
22a6d42e7dSPeter Dunlap /*
234558d122SViswanathan Kannappan * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24*0b905b49SYuri Pankov * Copyright 2017 Nexenta Systems, Inc.
25dd647b3dSAlexander Stetsenko */
26a6d42e7dSPeter Dunlap
27a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
28a6d42e7dSPeter Dunlap #include <sys/types.h>
29a6d42e7dSPeter Dunlap #include <sys/conf.h>
30a6d42e7dSPeter Dunlap #include <sys/file.h>
31a6d42e7dSPeter Dunlap #include <sys/ddi.h>
32a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
33a6d42e7dSPeter Dunlap #include <sys/modctl.h>
34a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
35a6d42e7dSPeter Dunlap #include <sys/socket.h>
36a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
37a6d42e7dSPeter Dunlap #include <inet/tcp.h>
38a6d42e7dSPeter Dunlap #include <sys/nvpair.h>
39a6d42e7dSPeter Dunlap
40a6d42e7dSPeter Dunlap #include <sys/stmf.h>
41a6d42e7dSPeter Dunlap #include <sys/stmf_ioctl.h>
42a6d42e7dSPeter Dunlap #include <sys/portif.h>
43a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
44a6d42e7dSPeter Dunlap #include <sys/idm/idm_conn_sm.h>
45a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h>
46a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
474558d122SViswanathan Kannappan
484558d122SViswanathan Kannappan #include "iscsit_isns.h"
494558d122SViswanathan Kannappan #include "iscsit.h"
50a6d42e7dSPeter Dunlap
51a6d42e7dSPeter Dunlap #define IPADDRSTRLEN INET6_ADDRSTRLEN /* space for ipaddr string */
52a6d42e7dSPeter Dunlap #define PORTALSTRLEN (IPADDRSTRLEN+16) /* add space for :port,tag */
53a6d42e7dSPeter Dunlap
544142b486SJames Moore void
554142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict);
564142b486SJames Moore
57a6d42e7dSPeter Dunlap static void
iscsit_bump_ttt(iscsit_conn_t * ict)584142b486SJames Moore iscsit_bump_ttt(iscsit_conn_t *ict)
59a6d42e7dSPeter Dunlap {
604142b486SJames Moore /*
614142b486SJames Moore * Set the target task tag. The value will be zero when
624142b486SJames Moore * the connection is created. Increment it and wrap it
634142b486SJames Moore * back to one if we hit the reserved value.
644142b486SJames Moore *
654142b486SJames Moore * The TTT is fabricated since there is no real task associated
664142b486SJames Moore * with a text request. The idm task range is reused here since
674142b486SJames Moore * no real tasks can be started from a discovery session and
684142b486SJames Moore * thus no conflicts are possible.
694142b486SJames Moore */
704142b486SJames Moore if (++ict->ict_text_rsp_ttt == IDM_TASKIDS_MAX)
714142b486SJames Moore ict->ict_text_rsp_ttt = 1;
724142b486SJames Moore }
73a6d42e7dSPeter Dunlap
744142b486SJames Moore static void
iscsit_text_resp_complete_cb(idm_pdu_t * pdu,idm_status_t status)754142b486SJames Moore iscsit_text_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status)
764142b486SJames Moore {
774142b486SJames Moore iscsit_conn_t *ict = pdu->isp_private;
784142b486SJames Moore
794142b486SJames Moore idm_pdu_free(pdu);
804142b486SJames Moore if (status != IDM_STATUS_SUCCESS) {
814142b486SJames Moore /*
824142b486SJames Moore * Could not send the last text response.
834142b486SJames Moore * Clear any state and bump the TTT so subsequent
844142b486SJames Moore * requests will not match.
854142b486SJames Moore */
864142b486SJames Moore iscsit_text_cmd_fini(ict);
874142b486SJames Moore iscsit_bump_ttt(ict);
88a6d42e7dSPeter Dunlap }
894142b486SJames Moore iscsit_conn_rele(ict);
904142b486SJames Moore }
91a6d42e7dSPeter Dunlap
924142b486SJames Moore static void
iscsit_text_reject(idm_pdu_t * req_pdu,uint8_t reason_code)934142b486SJames Moore iscsit_text_reject(idm_pdu_t *req_pdu, uint8_t reason_code)
944142b486SJames Moore {
954142b486SJames Moore iscsit_conn_t *ict = req_pdu->isp_ic->ic_handle;
964142b486SJames Moore
974142b486SJames Moore /*
984142b486SJames Moore * A reject means abandoning this text request.
994142b486SJames Moore * Cleanup any state from the request and increment the TTT
1004142b486SJames Moore * in case the initiator does not get the reject response
1014142b486SJames Moore * and attempts to resume this request.
1024142b486SJames Moore */
1034142b486SJames Moore iscsit_text_cmd_fini(ict);
1044142b486SJames Moore iscsit_bump_ttt(ict);
1054142b486SJames Moore iscsit_send_reject(ict, req_pdu, reason_code);
106a6d42e7dSPeter Dunlap idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS);
1074142b486SJames Moore
108a6d42e7dSPeter Dunlap }
109a6d42e7dSPeter Dunlap
1104142b486SJames Moore
1114142b486SJames Moore /*
1124142b486SJames Moore * Add individual <TargetAddress=ipaddr> tuple to the nvlist
1134142b486SJames Moore */
114a6d42e7dSPeter Dunlap static void
iscsit_add_portal(struct sockaddr_storage * ss,int tag,nvlist_t * nv_resp)11559927d31SYuri Pankov iscsit_add_portal(struct sockaddr_storage *ss, int tag, nvlist_t *nv_resp)
116a6d42e7dSPeter Dunlap {
1174142b486SJames Moore char ipaddr[IPADDRSTRLEN]; /* ip address string */
1184142b486SJames Moore char ta_value[PORTALSTRLEN]; /* target address value */
119a6d42e7dSPeter Dunlap struct sockaddr_in *sin;
1204142b486SJames Moore struct sockaddr_in6 *sin6;
121a6d42e7dSPeter Dunlap
1224142b486SJames Moore switch (ss->ss_family) {
1234142b486SJames Moore case AF_INET:
1244142b486SJames Moore sin = (struct sockaddr_in *)ss;
125*0b905b49SYuri Pankov (void) inet_ntop(AF_INET, &sin->sin_addr, ipaddr,
126*0b905b49SYuri Pankov sizeof (ipaddr));
1274142b486SJames Moore (void) snprintf(ta_value, sizeof (ta_value), "%s:%d,%d",
1284142b486SJames Moore ipaddr, ntohs(sin->sin_port), tag);
1294142b486SJames Moore break;
1304142b486SJames Moore case AF_INET6:
1314142b486SJames Moore sin6 = (struct sockaddr_in6 *)ss;
13259927d31SYuri Pankov (void) inet_ntop(AF_INET6, &sin6->sin6_addr, ipaddr,
13359927d31SYuri Pankov sizeof (ipaddr));
1344142b486SJames Moore (void) snprintf(ta_value, sizeof (ta_value), "[%s]:%d,%d",
1354142b486SJames Moore ipaddr, ntohs(sin6->sin6_port), tag);
1364142b486SJames Moore break;
1374142b486SJames Moore default:
1384142b486SJames Moore ASSERT(0);
1394142b486SJames Moore return;
1404142b486SJames Moore }
1414142b486SJames Moore (void) nvlist_add_string(nv_resp, "TargetAddress", ta_value);
1424142b486SJames Moore }
1434142b486SJames Moore
1444142b486SJames Moore /*
1454142b486SJames Moore * Process the special case of the default portal group.
1464142b486SJames Moore * Network addresses are obtained from the network stack and
1474142b486SJames Moore * require some reformatting.
1484142b486SJames Moore */
1494142b486SJames Moore static void
iscsit_add_default_portals(iscsit_conn_t * ict,idm_addr_list_t * ipaddr_p,nvlist_t * nv_resp)1504142b486SJames Moore iscsit_add_default_portals(iscsit_conn_t *ict, idm_addr_list_t *ipaddr_p,
1514142b486SJames Moore nvlist_t *nv_resp)
1524142b486SJames Moore {
1534142b486SJames Moore int pass, i;
1544142b486SJames Moore idm_addr_t *tip;
1554142b486SJames Moore struct sockaddr_storage ss;
1564142b486SJames Moore struct sockaddr_in *sin;
1574142b486SJames Moore struct sockaddr_in6 *sin6;
1584142b486SJames Moore
1594142b486SJames Moore /*
1604142b486SJames Moore * If this request was received on one of the portals,
1614142b486SJames Moore * output that portal first. Most initiators will try to
1624142b486SJames Moore * connect on the first portal in the SendTargets response.
1634142b486SJames Moore * For example, this will avoid the confusing situation of a
1644142b486SJames Moore * discovery coming in on an IB interface and the initiator
1654142b486SJames Moore * then doing the normal login on an ethernet interface.
1664142b486SJames Moore */
1674142b486SJames Moore sin = (struct sockaddr_in *)&ss;
1684142b486SJames Moore sin6 = (struct sockaddr_in6 *)&ss;
1694142b486SJames Moore for (pass = 1; pass <= 2; pass++) {
1704142b486SJames Moore tip = &ipaddr_p->al_addrs[0];
1714142b486SJames Moore for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) {
1724142b486SJames Moore /* Convert the address into sockaddr_storage format */
1734142b486SJames Moore switch (tip->a_addr.i_insize) {
1744142b486SJames Moore case sizeof (struct in_addr):
1754142b486SJames Moore sin->sin_family = AF_INET;
1764142b486SJames Moore sin->sin_port = htons(ISCSI_LISTEN_PORT);
1774142b486SJames Moore sin->sin_addr = tip->a_addr.i_addr.in4;
1784142b486SJames Moore break;
1794142b486SJames Moore case sizeof (struct in6_addr):
1804142b486SJames Moore sin6->sin6_family = AF_INET6;
1814142b486SJames Moore sin6->sin6_port = htons(ISCSI_LISTEN_PORT);
1824142b486SJames Moore sin6->sin6_addr = tip->a_addr.i_addr.in6;
1834142b486SJames Moore break;
1844142b486SJames Moore default:
1854142b486SJames Moore ASSERT(0);
1864142b486SJames Moore continue;
187a6d42e7dSPeter Dunlap }
1884142b486SJames Moore switch (pass) {
1894142b486SJames Moore case 1:
1904142b486SJames Moore /*
1914142b486SJames Moore * On the first pass, skip portals that
1924142b486SJames Moore * do not match the incoming connection.
1934142b486SJames Moore */
1944142b486SJames Moore if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
195bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) != 0)
1964142b486SJames Moore continue;
1974142b486SJames Moore break;
1984142b486SJames Moore case 2:
199a6d42e7dSPeter Dunlap /*
2004142b486SJames Moore * On the second pass, process the
2014142b486SJames Moore * remaining portals.
202a6d42e7dSPeter Dunlap */
2034142b486SJames Moore if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
204bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) == 0)
2054142b486SJames Moore continue;
2064142b486SJames Moore break;
207a6d42e7dSPeter Dunlap }
208a6d42e7dSPeter Dunlap /*
2094142b486SJames Moore * Add portal to the response list.
2104142b486SJames Moore * By convention, the default portal group tag == 1
211a6d42e7dSPeter Dunlap */
21259927d31SYuri Pankov iscsit_add_portal(&ss, 1, nv_resp);
213a6d42e7dSPeter Dunlap }
2144142b486SJames Moore }
2154142b486SJames Moore }
2164142b486SJames Moore
2174142b486SJames Moore /*
2184142b486SJames Moore * Process a portal group from the configuration database.
2194142b486SJames Moore */
2204142b486SJames Moore static void
iscsit_add_portals(iscsit_conn_t * ict,iscsit_tpgt_t * tpg_list,nvlist_t * nv_resp)2214142b486SJames Moore iscsit_add_portals(iscsit_conn_t *ict, iscsit_tpgt_t *tpg_list,
2224142b486SJames Moore nvlist_t *nv_resp)
2234142b486SJames Moore {
2244142b486SJames Moore int pass;
2254142b486SJames Moore iscsit_portal_t *portal, *next_portal;
2264142b486SJames Moore iscsit_tpg_t *tpg;
2274142b486SJames Moore struct sockaddr_storage *ss;
2284142b486SJames Moore
2294142b486SJames Moore /*
2304142b486SJames Moore * As with the default portal group, output the portal used by
2314142b486SJames Moore * the incoming request first.
2324142b486SJames Moore */
2334142b486SJames Moore tpg = tpg_list->tpgt_tpg;
2344142b486SJames Moore for (pass = 1; pass <= 2; pass++) {
2354142b486SJames Moore for (portal = avl_first(&tpg->tpg_portal_list);
2364142b486SJames Moore portal != NULL;
2374142b486SJames Moore portal = next_portal) {
2384142b486SJames Moore
2394142b486SJames Moore next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
240a6d42e7dSPeter Dunlap ss = &portal->portal_addr;
2414142b486SJames Moore switch (pass) {
2424142b486SJames Moore case 1:
2434142b486SJames Moore /*
2444142b486SJames Moore * On the first pass, skip portals that
2454142b486SJames Moore * do not match the incoming connection.
2464142b486SJames Moore */
247bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
248bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) != 0)
2494142b486SJames Moore continue;
250a6d42e7dSPeter Dunlap break;
2514142b486SJames Moore case 2:
2524142b486SJames Moore /*
2534142b486SJames Moore * On the second pass, process the
2544142b486SJames Moore * remaining portals.
2554142b486SJames Moore */
256bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
257bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) == 0)
2584142b486SJames Moore continue;
259a6d42e7dSPeter Dunlap break;
260a6d42e7dSPeter Dunlap }
26159927d31SYuri Pankov /* Add portal to the response list */
26259927d31SYuri Pankov iscsit_add_portal(ss, tpg_list->tpgt_tag, nv_resp);
2634142b486SJames Moore }
2644142b486SJames Moore }
2654142b486SJames Moore }
2664142b486SJames Moore
2674142b486SJames Moore /*
2684142b486SJames Moore * Process all the portal groups bound to a particular target.
2694142b486SJames Moore */
2704142b486SJames Moore static void
iscsit_add_tpgs(iscsit_conn_t * ict,iscsit_tgt_t * target,idm_addr_list_t * ipaddr_p,nvlist_t * nv_resp)2718c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target,
2728c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States idm_addr_list_t *ipaddr_p, nvlist_t *nv_resp)
2734142b486SJames Moore {
2744142b486SJames Moore iscsit_tpgt_t *tpg_list;
2754142b486SJames Moore
2764142b486SJames Moore /*
2774142b486SJames Moore * Look through the portal groups associated with this target.
2784142b486SJames Moore */
2794142b486SJames Moore mutex_enter(&target->target_mutex);
2804142b486SJames Moore tpg_list = avl_first(&target->target_tpgt_list);
2814142b486SJames Moore
2824142b486SJames Moore /* check for the default portal group */
2834142b486SJames Moore if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) {
2844142b486SJames Moore /*
2854142b486SJames Moore * The default portal group is a special case and will
2864142b486SJames Moore * return all reasonable interfaces on this node.
2874142b486SJames Moore *
2884142b486SJames Moore * A target cannot be bound to other portal groups
2894142b486SJames Moore * if it is bound to the default portal group.
2904142b486SJames Moore */
2914142b486SJames Moore ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL);
2924142b486SJames Moore
2938c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (ipaddr_p != NULL) {
2944142b486SJames Moore /* convert the ip address list to nvlist format */
2954142b486SJames Moore iscsit_add_default_portals(ict, ipaddr_p, nv_resp);
296a6d42e7dSPeter Dunlap }
2974142b486SJames Moore mutex_exit(&target->target_mutex);
2984142b486SJames Moore return;
2994142b486SJames Moore }
3004142b486SJames Moore
3014142b486SJames Moore /*
3024142b486SJames Moore * Not the default portal group - process the user defined tpgs
3034142b486SJames Moore */
3044142b486SJames Moore ASSERT(tpg_list != NULL);
3054142b486SJames Moore while (tpg_list != NULL) {
3064142b486SJames Moore
3074142b486SJames Moore ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg);
3084142b486SJames Moore
3094142b486SJames Moore /*
3104142b486SJames Moore * Found a defined portal group - add each portal address.
3114142b486SJames Moore * As with the default portal group, make 2 passes over
3124142b486SJames Moore * the addresses in order to output the connection
3134142b486SJames Moore * address first.
3144142b486SJames Moore */
3154142b486SJames Moore iscsit_add_portals(ict, tpg_list, nv_resp);
3164142b486SJames Moore
317a6d42e7dSPeter Dunlap tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list);
318a6d42e7dSPeter Dunlap }
319a6d42e7dSPeter Dunlap mutex_exit(&target->target_mutex);
320a6d42e7dSPeter Dunlap }
321a6d42e7dSPeter Dunlap
3224142b486SJames Moore #ifdef DEBUG
3234142b486SJames Moore /*
3244142b486SJames Moore * To test with smaller PDUs in order to force multi-PDU responses,
3254142b486SJames Moore * set this value such that: 0 < test_max_len < 8192
3264142b486SJames Moore */
327dd647b3dSAlexander Stetsenko uint32_t iscsit_text_max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
3284142b486SJames Moore #endif
3294142b486SJames Moore
3304142b486SJames Moore /*
3314142b486SJames Moore * Format a text response PDU from the text buffer and send it.
3324142b486SJames Moore */
3334142b486SJames Moore static void
iscsit_send_next_text_response(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)3344142b486SJames Moore iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
335a6d42e7dSPeter Dunlap {
336a6d42e7dSPeter Dunlap iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
337a6d42e7dSPeter Dunlap iscsi_text_rsp_hdr_t *th_resp;
3384142b486SJames Moore idm_pdu_t *resp;
3394142b486SJames Moore uint32_t len, remainder, max_len;
340dd647b3dSAlexander Stetsenko char *base;
341dd647b3dSAlexander Stetsenko boolean_t final;
3424142b486SJames Moore
3434142b486SJames Moore max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
3444142b486SJames Moore #ifdef DEBUG
345dd647b3dSAlexander Stetsenko if (iscsit_text_max_len > 0 && iscsit_text_max_len < max_len)
3464142b486SJames Moore max_len = iscsit_text_max_len;
3474142b486SJames Moore #endif
348dd647b3dSAlexander Stetsenko do {
349dd647b3dSAlexander Stetsenko remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off;
350dd647b3dSAlexander Stetsenko if (remainder <= max_len) {
351dd647b3dSAlexander Stetsenko len = remainder;
352dd647b3dSAlexander Stetsenko final = B_TRUE;
353dd647b3dSAlexander Stetsenko } else {
354dd647b3dSAlexander Stetsenko len = max_len;
355dd647b3dSAlexander Stetsenko final = B_FALSE;
356dd647b3dSAlexander Stetsenko }
357dd647b3dSAlexander Stetsenko /*
358dd647b3dSAlexander Stetsenko * Allocate a PDU and copy in text response buffer
359dd647b3dSAlexander Stetsenko */
360dd647b3dSAlexander Stetsenko resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len);
361dd647b3dSAlexander Stetsenko idm_pdu_init(resp, ict->ict_ic, ict,
362dd647b3dSAlexander Stetsenko iscsit_text_resp_complete_cb);
363dd647b3dSAlexander Stetsenko /* Advance the StatSN for each Text Response sent */
364dd647b3dSAlexander Stetsenko resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
365dd647b3dSAlexander Stetsenko base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off;
366dd647b3dSAlexander Stetsenko bcopy(base, resp->isp_data, len);
367dd647b3dSAlexander Stetsenko /*
368dd647b3dSAlexander Stetsenko * Fill in the response header
369dd647b3dSAlexander Stetsenko */
370dd647b3dSAlexander Stetsenko th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr;
371dd647b3dSAlexander Stetsenko bzero(th_resp, sizeof (*th_resp));
372dd647b3dSAlexander Stetsenko th_resp->opcode = ISCSI_OP_TEXT_RSP;
373dd647b3dSAlexander Stetsenko th_resp->itt = th_req->itt;
374dd647b3dSAlexander Stetsenko hton24(th_resp->dlength, len);
375dd647b3dSAlexander Stetsenko if (final) {
376dd647b3dSAlexander Stetsenko th_resp->flags = ISCSI_FLAG_FINAL;
377dd647b3dSAlexander Stetsenko th_resp->ttt = ISCSI_RSVD_TASK_TAG;
378dd647b3dSAlexander Stetsenko kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
379dd647b3dSAlexander Stetsenko ict->ict_text_rsp_buf = NULL;
380dd647b3dSAlexander Stetsenko ict->ict_text_rsp_len = 0;
381dd647b3dSAlexander Stetsenko ict->ict_text_rsp_valid_len = 0;
382dd647b3dSAlexander Stetsenko ict->ict_text_rsp_off = 0;
383dd647b3dSAlexander Stetsenko } else {
384dd647b3dSAlexander Stetsenko th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE;
385dd647b3dSAlexander Stetsenko th_resp->ttt = ict->ict_text_rsp_ttt;
386dd647b3dSAlexander Stetsenko ict->ict_text_rsp_off += len;
387dd647b3dSAlexander Stetsenko }
388dd647b3dSAlexander Stetsenko /* Send the response on its way */
389dd647b3dSAlexander Stetsenko iscsit_conn_hold(ict);
390dd647b3dSAlexander Stetsenko iscsit_pdu_tx(resp);
391dd647b3dSAlexander Stetsenko } while (!final);
3924142b486SJames Moore /* Free the request pdu */
3934142b486SJames Moore idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
3944142b486SJames Moore }
3954142b486SJames Moore
3964142b486SJames Moore /*
3974142b486SJames Moore * Clean-up the text buffer if it exists.
3984142b486SJames Moore */
3994142b486SJames Moore void
iscsit_text_cmd_fini(iscsit_conn_t * ict)4004142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict)
4014142b486SJames Moore {
4024142b486SJames Moore if (ict->ict_text_rsp_buf != NULL) {
4034142b486SJames Moore ASSERT(ict->ict_text_rsp_len != 0);
4044142b486SJames Moore kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
4054142b486SJames Moore }
4064142b486SJames Moore ict->ict_text_rsp_buf = NULL;
4074142b486SJames Moore ict->ict_text_rsp_len = 0;
4084142b486SJames Moore ict->ict_text_rsp_valid_len = 0;
4094142b486SJames Moore ict->ict_text_rsp_off = 0;
4104142b486SJames Moore }
4114142b486SJames Moore
4124142b486SJames Moore /*
4134142b486SJames Moore * Process an iSCSI text command.
4144142b486SJames Moore *
4154142b486SJames Moore * This code only handles the common case of a text command
4164142b486SJames Moore * containing the single tuple SendTargets=All issued during
4174142b486SJames Moore * a discovery session. The request will always arrive in a
4184142b486SJames Moore * single PDU, but the response may span multiple PDUs if the
4194142b486SJames Moore * configuration is large. I.e. many targets and portals.
4204142b486SJames Moore *
4214142b486SJames Moore * The request is checked for correctness and then the response
4224142b486SJames Moore * is generated from the global target into nvlist format. Then
4234142b486SJames Moore * the nvlist is reformatted into idm textbuf format which reflects
4244142b486SJames Moore * the iSCSI defined <name=value> specification. Finally, the
4254142b486SJames Moore * textbuf is sent to the initiator in one or more text response PDUs
4264142b486SJames Moore */
4274142b486SJames Moore void
iscsit_pdu_op_text_cmd(iscsit_conn_t * ict,idm_pdu_t * rx_pdu)4284142b486SJames Moore iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
4294142b486SJames Moore {
4304142b486SJames Moore iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
431a6d42e7dSPeter Dunlap nvlist_t *nv_resp;
4324142b486SJames Moore char *kv_pair;
433a6d42e7dSPeter Dunlap int flags;
4344142b486SJames Moore char *textbuf;
435a6d42e7dSPeter Dunlap int textbuflen;
4364142b486SJames Moore int validlen;
437a6d42e7dSPeter Dunlap int rc;
438a6d42e7dSPeter Dunlap
439a6d42e7dSPeter Dunlap flags = th_req->flags;
440a6d42e7dSPeter Dunlap if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) {
4414142b486SJames Moore /* Cannot handle multi-PDU requests now */
4424142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
443a6d42e7dSPeter Dunlap return;
444a6d42e7dSPeter Dunlap }
445a6d42e7dSPeter Dunlap if (th_req->ttt != ISCSI_RSVD_TASK_TAG) {
4464142b486SJames Moore /*
4474142b486SJames Moore * This is the initiator acknowledging our last PDU and
4484142b486SJames Moore * indicating it is ready for the next PDU in the sequence.
4494142b486SJames Moore */
4504142b486SJames Moore /*
4514142b486SJames Moore * There can only be one outstanding text request on a
4524142b486SJames Moore * connection. Make sure this one PDU has the current TTT.
4534142b486SJames Moore */
4544142b486SJames Moore /* XXX combine the following 3 checks after testing */
4554142b486SJames Moore if (th_req->ttt != ict->ict_text_rsp_ttt) {
4564142b486SJames Moore /* Not part of this sequence */
4574142b486SJames Moore iscsit_text_reject(rx_pdu,
4584142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED);
4594142b486SJames Moore return;
4604142b486SJames Moore }
4614142b486SJames Moore /*
4624142b486SJames Moore * ITT should match what was saved from first PDU.
4634142b486SJames Moore */
4644142b486SJames Moore if (th_req->itt != ict->ict_text_req_itt) {
4654142b486SJames Moore /* Not part of this sequence */
4664142b486SJames Moore iscsit_text_reject(rx_pdu,
4674142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED);
4684142b486SJames Moore return;
4694142b486SJames Moore }
4704142b486SJames Moore /*
4714142b486SJames Moore * Cannot deal with more key/value pairs now.
4724142b486SJames Moore */
4734142b486SJames Moore if (rx_pdu->isp_datalen != 0) {
4744142b486SJames Moore iscsit_text_reject(rx_pdu,
4754142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED);
4764142b486SJames Moore return;
4774142b486SJames Moore }
4784142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu);
479a6d42e7dSPeter Dunlap return;
480a6d42e7dSPeter Dunlap }
481a6d42e7dSPeter Dunlap
482a6d42e7dSPeter Dunlap /*
4834142b486SJames Moore * Initiator has started a new text request. Only
4844142b486SJames Moore * one can be active at a time, so abandon any previous
4854142b486SJames Moore * text request on this connection.
486a6d42e7dSPeter Dunlap */
4874142b486SJames Moore iscsit_text_cmd_fini(ict);
4884142b486SJames Moore
4894142b486SJames Moore /* Set the target task tag. */
4904142b486SJames Moore iscsit_bump_ttt(ict);
491a6d42e7dSPeter Dunlap
4924142b486SJames Moore /* Save the initiator task tag */
4934142b486SJames Moore ict->ict_text_req_itt = th_req->itt;
4944142b486SJames Moore
4954142b486SJames Moore /*
4964142b486SJames Moore * Make sure this is a proper SendTargets request
4974142b486SJames Moore */
498a6d42e7dSPeter Dunlap textbuf = (char *)rx_pdu->isp_data;
499a6d42e7dSPeter Dunlap textbuflen = rx_pdu->isp_datalen;
500a6d42e7dSPeter Dunlap kv_pair = "SendTargets=All";
5014142b486SJames Moore if (textbuflen >= strlen(kv_pair) &&
5024142b486SJames Moore strcmp(kv_pair, textbuf) == 0 &&
503a6d42e7dSPeter Dunlap ict->ict_op.op_discovery_session == B_TRUE) {
504a6d42e7dSPeter Dunlap /*
505a6d42e7dSPeter Dunlap * Most common case of SendTargets=All during discovery.
506a6d42e7dSPeter Dunlap */
507483b029bSYuri Pankov idm_addr_list_t *ipaddr_p;
508483b029bSYuri Pankov iscsit_tgt_t *tgt, *ntgt;
509483b029bSYuri Pankov int ipsize;
510483b029bSYuri Pankov
511483b029bSYuri Pankov
512483b029bSYuri Pankov /* Create an nvlist for response */
513a6d42e7dSPeter Dunlap if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) {
5144142b486SJames Moore iscsit_text_reject(rx_pdu,
515a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED);
516a6d42e7dSPeter Dunlap return;
517a6d42e7dSPeter Dunlap }
518a6d42e7dSPeter Dunlap
519483b029bSYuri Pankov /* Get the list of local interface addresses */
5208c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States ipsize = idm_get_ipaddr(&ipaddr_p);
5218c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States
522483b029bSYuri Pankov /* Add targets to the response list */
523a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_LOCK(RW_READER);
524483b029bSYuri Pankov for (tgt = avl_first(&iscsit_global.global_target_list);
525483b029bSYuri Pankov tgt != NULL; tgt = ntgt) {
526483b029bSYuri Pankov struct sockaddr_storage v4sa, *sa;
5274142b486SJames Moore iscsit_tgt_state_t state;
528483b029bSYuri Pankov iscsit_portal_t *portal;
529483b029bSYuri Pankov iscsit_tpgt_t *tpgt;
5304142b486SJames Moore
531483b029bSYuri Pankov ntgt = AVL_NEXT(&iscsit_global.global_target_list, tgt);
5324142b486SJames Moore
533483b029bSYuri Pankov /* Only report online and onlining targets */
534483b029bSYuri Pankov state = tgt->target_state;
5354142b486SJames Moore if (state != TS_ONLINING && state != TS_ONLINE &&
5364142b486SJames Moore state != TS_STMF_ONLINE)
5374142b486SJames Moore continue;
5384142b486SJames Moore
539483b029bSYuri Pankov /*
540483b029bSYuri Pankov * Report target if:
541483b029bSYuri Pankov * - it is bound to default TPG
542483b029bSYuri Pankov * - one of the addresses of TPGs the target is bound
543483b029bSYuri Pankov * to matches incoming connection dst address
544483b029bSYuri Pankov */
545483b029bSYuri Pankov sa = &ict->ict_ic->ic_laddr;
546483b029bSYuri Pankov mutex_enter(&tgt->target_mutex);
547483b029bSYuri Pankov tpgt = avl_first(&tgt->target_tpgt_list);
548483b029bSYuri Pankov if (!(IS_DEFAULT_TPGT(tpgt))) {
549483b029bSYuri Pankov portal = iscsit_tgt_lookup_portal(tgt, sa,
550483b029bSYuri Pankov &tpgt);
551483b029bSYuri Pankov if (portal == NULL &&
552483b029bSYuri Pankov iscsit_is_v4_mapped(sa, &v4sa)) {
553483b029bSYuri Pankov portal = iscsit_tgt_lookup_portal(tgt,
554483b029bSYuri Pankov &v4sa, &tpgt);
555483b029bSYuri Pankov }
556483b029bSYuri Pankov if (portal == NULL) {
557483b029bSYuri Pankov mutex_exit(&tgt->target_mutex);
558483b029bSYuri Pankov continue;
559483b029bSYuri Pankov }
560483b029bSYuri Pankov iscsit_portal_rele(portal);
561483b029bSYuri Pankov iscsit_tpgt_rele(tpgt);
562483b029bSYuri Pankov }
563483b029bSYuri Pankov mutex_exit(&tgt->target_mutex);
564483b029bSYuri Pankov
565483b029bSYuri Pankov if (nvlist_add_string(nv_resp, "TargetName",
566483b029bSYuri Pankov tgt->target_name) == 0) {
567483b029bSYuri Pankov /* Add the portal groups bound to this target */
568483b029bSYuri Pankov iscsit_add_tpgs(ict, tgt, ipaddr_p, nv_resp);
5694142b486SJames Moore }
570a6d42e7dSPeter Dunlap }
571a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_UNLOCK();
572483b029bSYuri Pankov if (ipsize > 0)
5738c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States kmem_free(ipaddr_p, ipsize);
574a6d42e7dSPeter Dunlap
575483b029bSYuri Pankov /* Convert the response nvlist into an idm text buffer */
576a6d42e7dSPeter Dunlap textbuf = 0;
577a6d42e7dSPeter Dunlap textbuflen = 0;
578a6d42e7dSPeter Dunlap validlen = 0;
579a6d42e7dSPeter Dunlap rc = idm_nvlist_to_textbuf(nv_resp, &textbuf,
580a6d42e7dSPeter Dunlap &textbuflen, &validlen);
581a6d42e7dSPeter Dunlap nvlist_free(nv_resp);
582a6d42e7dSPeter Dunlap if (rc != 0) {
583a6d42e7dSPeter Dunlap if (textbuf && textbuflen)
584a6d42e7dSPeter Dunlap kmem_free(textbuf, textbuflen);
5854142b486SJames Moore iscsit_text_reject(rx_pdu,
586a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED);
587a6d42e7dSPeter Dunlap return;
588a6d42e7dSPeter Dunlap }
5894142b486SJames Moore ict->ict_text_rsp_buf = textbuf;
5904142b486SJames Moore ict->ict_text_rsp_len = textbuflen;
5914142b486SJames Moore ict->ict_text_rsp_valid_len = validlen;
5924142b486SJames Moore ict->ict_text_rsp_off = 0;
5934142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu);
594a6d42e7dSPeter Dunlap } else {
595a6d42e7dSPeter Dunlap /*
596a6d42e7dSPeter Dunlap * Other cases to handle
597a6d42e7dSPeter Dunlap * Discovery session:
598a6d42e7dSPeter Dunlap * SendTargets=<target_name>
599a6d42e7dSPeter Dunlap * Normal session
600a6d42e7dSPeter Dunlap * SendTargets=<NULL> - assume target name of session
601a6d42e7dSPeter Dunlap * All others
602a6d42e7dSPeter Dunlap * Error
603a6d42e7dSPeter Dunlap */
6044142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
605a6d42e7dSPeter Dunlap return;
606a6d42e7dSPeter Dunlap }
607a6d42e7dSPeter Dunlap }
608