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  */
21a6d42e7dSPeter Dunlap /*
22*4558d122SViswanathan Kannappan  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23a6d42e7dSPeter Dunlap  */
24a6d42e7dSPeter Dunlap 
25a6d42e7dSPeter Dunlap #include <sys/cpuvar.h>
26a6d42e7dSPeter Dunlap #include <sys/types.h>
27a6d42e7dSPeter Dunlap #include <sys/conf.h>
28a6d42e7dSPeter Dunlap #include <sys/file.h>
29a6d42e7dSPeter Dunlap #include <sys/ddi.h>
30a6d42e7dSPeter Dunlap #include <sys/sunddi.h>
31a6d42e7dSPeter Dunlap #include <sys/modctl.h>
32a6d42e7dSPeter Dunlap #include <sys/sysmacros.h>
33a6d42e7dSPeter Dunlap #include <sys/socket.h>
34a6d42e7dSPeter Dunlap #include <sys/strsubr.h>
35a6d42e7dSPeter Dunlap #include <inet/tcp.h>
36a6d42e7dSPeter Dunlap #include <sys/nvpair.h>
37a6d42e7dSPeter Dunlap 
38a6d42e7dSPeter Dunlap #include <sys/stmf.h>
39a6d42e7dSPeter Dunlap #include <sys/stmf_ioctl.h>
40a6d42e7dSPeter Dunlap #include <sys/portif.h>
41a6d42e7dSPeter Dunlap #include <sys/idm/idm.h>
42a6d42e7dSPeter Dunlap #include <sys/idm/idm_conn_sm.h>
43a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h>
44a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h>
45*4558d122SViswanathan Kannappan 
46*4558d122SViswanathan Kannappan #include "iscsit_isns.h"
47*4558d122SViswanathan Kannappan #include "iscsit.h"
48a6d42e7dSPeter Dunlap 
49a6d42e7dSPeter Dunlap #define	IPADDRSTRLEN	INET6_ADDRSTRLEN	/* space for ipaddr string */
50a6d42e7dSPeter Dunlap #define	PORTALSTRLEN	(IPADDRSTRLEN+16)	/* add space for :port,tag */
51a6d42e7dSPeter Dunlap 
524142b486SJames Moore void
534142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict);
544142b486SJames Moore 
55a6d42e7dSPeter Dunlap /*
56a6d42e7dSPeter Dunlap  * The kernel inet_ntop() function formats ipv4 address fields with
57a6d42e7dSPeter Dunlap  * leading zeros which the win2k initiator interprets as octal.
58a6d42e7dSPeter Dunlap  */
59a6d42e7dSPeter Dunlap 
60a6d42e7dSPeter Dunlap static void iscsit_v4_ntop(struct in_addr *in, char a[], int size)
61a6d42e7dSPeter Dunlap {
62a6d42e7dSPeter Dunlap 	unsigned char *p = (unsigned char *) in;
63a6d42e7dSPeter Dunlap 
64a6d42e7dSPeter Dunlap 	(void) snprintf(a, size, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3));
65a6d42e7dSPeter Dunlap }
66a6d42e7dSPeter Dunlap 
67a6d42e7dSPeter Dunlap static void
684142b486SJames Moore iscsit_bump_ttt(iscsit_conn_t *ict)
69a6d42e7dSPeter Dunlap {
704142b486SJames Moore 	/*
714142b486SJames Moore 	 * Set the target task tag. The value will be zero when
724142b486SJames Moore 	 * the connection is created. Increment it and wrap it
734142b486SJames Moore 	 * back to one if we hit the reserved value.
744142b486SJames Moore 	 *
754142b486SJames Moore 	 * The TTT is fabricated since there is no real task associated
764142b486SJames Moore 	 * with a text request. The idm task range is reused here since
774142b486SJames Moore 	 * no real tasks can be started from a discovery session and
784142b486SJames Moore 	 * thus no conflicts are possible.
794142b486SJames Moore 	 */
804142b486SJames Moore 	if (++ict->ict_text_rsp_ttt == IDM_TASKIDS_MAX)
814142b486SJames Moore 		ict->ict_text_rsp_ttt = 1;
824142b486SJames Moore }
83a6d42e7dSPeter Dunlap 
844142b486SJames Moore static void
854142b486SJames Moore iscsit_text_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status)
864142b486SJames Moore {
874142b486SJames Moore 	iscsit_conn_t *ict = pdu->isp_private;
884142b486SJames Moore 
894142b486SJames Moore 	idm_pdu_free(pdu);
904142b486SJames Moore 	if (status != IDM_STATUS_SUCCESS) {
914142b486SJames Moore 		/*
924142b486SJames Moore 		 * Could not send the last text response.
934142b486SJames Moore 		 * Clear any state and bump the TTT so subsequent
944142b486SJames Moore 		 * requests will not match.
954142b486SJames Moore 		 */
964142b486SJames Moore 		iscsit_text_cmd_fini(ict);
974142b486SJames Moore 		iscsit_bump_ttt(ict);
98a6d42e7dSPeter Dunlap 	}
994142b486SJames Moore 	iscsit_conn_rele(ict);
1004142b486SJames Moore }
101a6d42e7dSPeter Dunlap 
1024142b486SJames Moore static void
1034142b486SJames Moore iscsit_text_reject(idm_pdu_t *req_pdu, uint8_t reason_code)
1044142b486SJames Moore {
1054142b486SJames Moore 	iscsit_conn_t		*ict = req_pdu->isp_ic->ic_handle;
1064142b486SJames Moore 
1074142b486SJames Moore 	/*
1084142b486SJames Moore 	 * A reject means abandoning this text request.
1094142b486SJames Moore 	 * Cleanup any state from the request and increment the TTT
1104142b486SJames Moore 	 * in case the initiator does not get the reject response
1114142b486SJames Moore 	 * and attempts to resume this request.
1124142b486SJames Moore 	 */
1134142b486SJames Moore 	iscsit_text_cmd_fini(ict);
1144142b486SJames Moore 	iscsit_bump_ttt(ict);
1154142b486SJames Moore 	iscsit_send_reject(ict, req_pdu, reason_code);
116a6d42e7dSPeter Dunlap 	idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS);
1174142b486SJames Moore 
118a6d42e7dSPeter Dunlap }
119a6d42e7dSPeter Dunlap 
1204142b486SJames Moore 
1214142b486SJames Moore /*
1224142b486SJames Moore  * Add individual <TargetAddress=ipaddr> tuple to the nvlist
1234142b486SJames Moore  */
124a6d42e7dSPeter Dunlap static void
1254142b486SJames Moore iscsit_add_portal(struct sockaddr_storage *ss, int flip_v6, int tag,
1264142b486SJames Moore     nvlist_t *nv_resp)
127a6d42e7dSPeter Dunlap {
1284142b486SJames Moore 	char ipaddr[IPADDRSTRLEN];	/* ip address string */
1294142b486SJames Moore 	char ta_value[PORTALSTRLEN];	/* target address value */
130a6d42e7dSPeter Dunlap 	struct sockaddr_in *sin;
131a6d42e7dSPeter Dunlap 	struct in_addr *in;
1324142b486SJames Moore 	struct sockaddr_in6 *sin6;
1334142b486SJames Moore 	struct in6_addr *in6, flip_in6;
134a6d42e7dSPeter Dunlap 
1354142b486SJames Moore 	switch (ss->ss_family) {
1364142b486SJames Moore 	case AF_INET:
1374142b486SJames Moore 		sin = (struct sockaddr_in *)ss;
1384142b486SJames Moore 		in = &sin->sin_addr;
1394142b486SJames Moore 		iscsit_v4_ntop(in, ipaddr, sizeof (ipaddr));
1404142b486SJames Moore 		(void) snprintf(ta_value, sizeof (ta_value), "%s:%d,%d",
1414142b486SJames Moore 		    ipaddr, ntohs(sin->sin_port), tag);
1424142b486SJames Moore 		break;
1434142b486SJames Moore 	case AF_INET6:
1444142b486SJames Moore 		sin6 = (struct sockaddr_in6 *)ss;
1454142b486SJames Moore 		in6 = &sin6->sin6_addr;
1464142b486SJames Moore 		if (flip_v6) {
1474142b486SJames Moore 			uint16_t *v6_field_i = (uint16_t *)in6;
1484142b486SJames Moore 			uint16_t *v6_field_o = (uint16_t *)&flip_in6;
1494142b486SJames Moore 			int i;
150a6d42e7dSPeter Dunlap 
151a6d42e7dSPeter Dunlap 			/*
1524142b486SJames Moore 			 * Ugh. The iSCSI config data is stored in host
1534142b486SJames Moore 			 * order while the addresses retrieved from the
1544142b486SJames Moore 			 * stack come back in network order. inet_ntop
1554142b486SJames Moore 			 * expects network order.
156a6d42e7dSPeter Dunlap 			 */
1574142b486SJames Moore 			for (i = 0; i < 8; i++)
1584142b486SJames Moore 				*v6_field_o++ = htons(*v6_field_i++);
1594142b486SJames Moore 			in6 = &flip_in6;
1604142b486SJames Moore 		}
1614142b486SJames Moore 		(void) inet_ntop(AF_INET6, in6, ipaddr, sizeof (ipaddr));
1624142b486SJames Moore 		(void) snprintf(ta_value, sizeof (ta_value), "[%s]:%d,%d",
1634142b486SJames Moore 		    ipaddr, ntohs(sin6->sin6_port), tag);
1644142b486SJames Moore 		break;
1654142b486SJames Moore 	default:
1664142b486SJames Moore 		ASSERT(0);
1674142b486SJames Moore 		return;
1684142b486SJames Moore 	}
1694142b486SJames Moore 	(void) nvlist_add_string(nv_resp, "TargetAddress", ta_value);
1704142b486SJames Moore }
1714142b486SJames Moore 
1724142b486SJames Moore /*
1734142b486SJames Moore  * Process the special case of the default portal group.
1744142b486SJames Moore  * Network addresses are obtained from the network stack and
1754142b486SJames Moore  * require some reformatting.
1764142b486SJames Moore  */
1774142b486SJames Moore static void
1784142b486SJames Moore iscsit_add_default_portals(iscsit_conn_t *ict, idm_addr_list_t *ipaddr_p,
1794142b486SJames Moore     nvlist_t *nv_resp)
1804142b486SJames Moore {
1814142b486SJames Moore 	int pass, i;
1824142b486SJames Moore 	idm_addr_t *tip;
1834142b486SJames Moore 	struct sockaddr_storage ss;
1844142b486SJames Moore 	struct sockaddr_in *sin;
1854142b486SJames Moore 	struct sockaddr_in6 *sin6;
1864142b486SJames Moore 
1874142b486SJames Moore 	/*
1884142b486SJames Moore 	 * If this request was received on one of the portals,
1894142b486SJames Moore 	 * output that portal first. Most initiators will try to
1904142b486SJames Moore 	 * connect on the first portal in the SendTargets response.
1914142b486SJames Moore 	 * For example, this will avoid the confusing situation of a
1924142b486SJames Moore 	 * discovery coming in on an IB interface and the initiator
1934142b486SJames Moore 	 * then doing the normal login on an ethernet interface.
1944142b486SJames Moore 	 */
1954142b486SJames Moore 	sin = (struct sockaddr_in *)&ss;
1964142b486SJames Moore 	sin6 = (struct sockaddr_in6 *)&ss;
1974142b486SJames Moore 	for (pass = 1; pass <= 2; pass++) {
1984142b486SJames Moore 		tip = &ipaddr_p->al_addrs[0];
1994142b486SJames Moore 		for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) {
2004142b486SJames Moore 			/* Convert the address into sockaddr_storage format */
2014142b486SJames Moore 			switch (tip->a_addr.i_insize) {
2024142b486SJames Moore 			case sizeof (struct in_addr):
2034142b486SJames Moore 				sin->sin_family = AF_INET;
2044142b486SJames Moore 				sin->sin_port = htons(ISCSI_LISTEN_PORT);
2054142b486SJames Moore 				sin->sin_addr = tip->a_addr.i_addr.in4;
2064142b486SJames Moore 				break;
2074142b486SJames Moore 			case sizeof (struct in6_addr):
2084142b486SJames Moore 				sin6->sin6_family = AF_INET6;
2094142b486SJames Moore 				sin6->sin6_port = htons(ISCSI_LISTEN_PORT);
2104142b486SJames Moore 				sin6->sin6_addr = tip->a_addr.i_addr.in6;
2114142b486SJames Moore 				break;
2124142b486SJames Moore 			default:
2134142b486SJames Moore 				ASSERT(0);
2144142b486SJames Moore 				continue;
215a6d42e7dSPeter Dunlap 			}
2164142b486SJames Moore 			switch (pass) {
2174142b486SJames Moore 			case 1:
2184142b486SJames Moore 				/*
2194142b486SJames Moore 				 * On the first pass, skip portals that
2204142b486SJames Moore 				 * do not match the incoming connection.
2214142b486SJames Moore 				 */
2224142b486SJames Moore 				if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
223bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				    B_TRUE, B_TRUE) != 0)
2244142b486SJames Moore 					continue;
2254142b486SJames Moore 				break;
2264142b486SJames Moore 			case 2:
227a6d42e7dSPeter Dunlap 				/*
2284142b486SJames Moore 				 * On the second pass, process the
2294142b486SJames Moore 				 * remaining portals.
230a6d42e7dSPeter Dunlap 				 */
2314142b486SJames Moore 				if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
232bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				    B_TRUE, B_TRUE) == 0)
2334142b486SJames Moore 					continue;
2344142b486SJames Moore 				break;
235a6d42e7dSPeter Dunlap 			}
236a6d42e7dSPeter Dunlap 			/*
2374142b486SJames Moore 			 * Add portal to the response list.
2384142b486SJames Moore 			 * Do not byte swap v6 address.
2394142b486SJames Moore 			 * By convention, the default portal group tag == 1
240a6d42e7dSPeter Dunlap 			 */
2414142b486SJames Moore 			iscsit_add_portal(&ss, 0, 1, nv_resp);
242a6d42e7dSPeter Dunlap 		}
2434142b486SJames Moore 	}
2444142b486SJames Moore }
2454142b486SJames Moore 
2464142b486SJames Moore /*
2474142b486SJames Moore  * Process a portal group from the configuration database.
2484142b486SJames Moore  */
2494142b486SJames Moore static void
2504142b486SJames Moore iscsit_add_portals(iscsit_conn_t *ict, iscsit_tpgt_t *tpg_list,
2514142b486SJames Moore     nvlist_t *nv_resp)
2524142b486SJames Moore {
2534142b486SJames Moore 	int pass;
2544142b486SJames Moore 	iscsit_portal_t *portal, *next_portal;
2554142b486SJames Moore 	iscsit_tpg_t *tpg;
2564142b486SJames Moore 	struct sockaddr_storage *ss;
2574142b486SJames Moore 
2584142b486SJames Moore 	/*
2594142b486SJames Moore 	 * As with the default portal group, output the portal used by
2604142b486SJames Moore 	 * the incoming request first.
2614142b486SJames Moore 	 */
2624142b486SJames Moore 	tpg = tpg_list->tpgt_tpg;
2634142b486SJames Moore 	for (pass = 1; pass <= 2; pass++) {
2644142b486SJames Moore 		for (portal = avl_first(&tpg->tpg_portal_list);
2654142b486SJames Moore 		    portal != NULL;
2664142b486SJames Moore 		    portal = next_portal) {
2674142b486SJames Moore 
2684142b486SJames Moore 			next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
269a6d42e7dSPeter Dunlap 			ss = &portal->portal_addr;
2704142b486SJames Moore 			switch (pass) {
2714142b486SJames Moore 			case 1:
2724142b486SJames Moore 				/*
2734142b486SJames Moore 				 * On the first pass, skip portals that
2744142b486SJames Moore 				 * do not match the incoming connection.
2754142b486SJames Moore 				 */
276bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
277bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				    B_TRUE, B_TRUE) != 0)
2784142b486SJames Moore 					continue;
279a6d42e7dSPeter Dunlap 				break;
2804142b486SJames Moore 			case 2:
2814142b486SJames Moore 				/*
2824142b486SJames Moore 				 * On the second pass, process the
2834142b486SJames Moore 				 * remaining portals.
2844142b486SJames Moore 				 */
285bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr,
286bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				    B_TRUE, B_TRUE) == 0)
2874142b486SJames Moore 					continue;
288a6d42e7dSPeter Dunlap 				break;
289a6d42e7dSPeter Dunlap 			}
2904142b486SJames Moore 			/*
2914142b486SJames Moore 			 * Add portal to the response list.
2924142b486SJames Moore 			 * Need to byte swap v6 address.
2934142b486SJames Moore 			 */
2944142b486SJames Moore 			iscsit_add_portal(ss, 1, tpg_list->tpgt_tag, nv_resp);
2954142b486SJames Moore 		}
2964142b486SJames Moore 	}
2974142b486SJames Moore }
2984142b486SJames Moore 
2994142b486SJames Moore /*
3004142b486SJames Moore  * Process all the portal groups bound to a particular target.
3014142b486SJames Moore  */
3024142b486SJames Moore static void
3038c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target,
3048c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States     idm_addr_list_t *ipaddr_p,  nvlist_t *nv_resp)
3054142b486SJames Moore {
3064142b486SJames Moore 	iscsit_tpgt_t *tpg_list;
3074142b486SJames Moore 
3084142b486SJames Moore 	/*
3094142b486SJames Moore 	 * Look through the portal groups associated with this target.
3104142b486SJames Moore 	 */
3114142b486SJames Moore 	mutex_enter(&target->target_mutex);
3124142b486SJames Moore 	tpg_list = avl_first(&target->target_tpgt_list);
3134142b486SJames Moore 
3144142b486SJames Moore 	/* check for the default portal group */
3154142b486SJames Moore 	if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) {
3164142b486SJames Moore 		/*
3174142b486SJames Moore 		 * The default portal group is a special case and will
3184142b486SJames Moore 		 * return all reasonable interfaces on this node.
3194142b486SJames Moore 		 *
3204142b486SJames Moore 		 * A target cannot be bound to other portal groups
3214142b486SJames Moore 		 * if it is bound to the default portal group.
3224142b486SJames Moore 		 */
3234142b486SJames Moore 		ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL);
3244142b486SJames Moore 
3258c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		if (ipaddr_p != NULL) {
3264142b486SJames Moore 			/* convert the ip address list to nvlist format */
3274142b486SJames Moore 			iscsit_add_default_portals(ict, ipaddr_p, nv_resp);
328a6d42e7dSPeter Dunlap 		}
3294142b486SJames Moore 		mutex_exit(&target->target_mutex);
3304142b486SJames Moore 		return;
3314142b486SJames Moore 	}
3324142b486SJames Moore 
3334142b486SJames Moore 	/*
3344142b486SJames Moore 	 * Not the default portal group - process the user defined tpgs
3354142b486SJames Moore 	 */
3364142b486SJames Moore 	ASSERT(tpg_list != NULL);
3374142b486SJames Moore 	while (tpg_list != NULL) {
3384142b486SJames Moore 
3394142b486SJames Moore 		ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg);
3404142b486SJames Moore 
3414142b486SJames Moore 		/*
3424142b486SJames Moore 		 * Found a defined portal group - add each portal address.
3434142b486SJames Moore 		 * As with the default portal group, make 2 passes over
3444142b486SJames Moore 		 * the addresses in order to output the connection
3454142b486SJames Moore 		 * address first.
3464142b486SJames Moore 		 */
3474142b486SJames Moore 		iscsit_add_portals(ict, tpg_list, nv_resp);
3484142b486SJames Moore 
349a6d42e7dSPeter Dunlap 		tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list);
350a6d42e7dSPeter Dunlap 	}
351a6d42e7dSPeter Dunlap 	mutex_exit(&target->target_mutex);
352a6d42e7dSPeter Dunlap }
353a6d42e7dSPeter Dunlap 
3544142b486SJames Moore #ifdef DEBUG
3554142b486SJames Moore /*
3564142b486SJames Moore  * To test with smaller PDUs in order to force multi-PDU responses,
3574142b486SJames Moore  * set this value such that: 0 < test_max_len < 8192
3584142b486SJames Moore  */
3594142b486SJames Moore uint32_t iscsit_text_max_len = 0;
3604142b486SJames Moore #endif
3614142b486SJames Moore 
3624142b486SJames Moore /*
3634142b486SJames Moore  * Format a text response PDU from the text buffer and send it.
3644142b486SJames Moore  */
3654142b486SJames Moore static void
3664142b486SJames Moore iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
367a6d42e7dSPeter Dunlap {
368a6d42e7dSPeter Dunlap 	iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
369a6d42e7dSPeter Dunlap 	iscsi_text_rsp_hdr_t *th_resp;
3704142b486SJames Moore 	idm_pdu_t	*resp;
3714142b486SJames Moore 	uint32_t	len, remainder, max_len;
3724142b486SJames Moore 	char 		*base;
3734142b486SJames Moore 	int		final;
3744142b486SJames Moore 
3754142b486SJames Moore 	max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
3764142b486SJames Moore #ifdef DEBUG
3774142b486SJames Moore 	if (iscsit_text_max_len > 0 && iscsit_text_max_len < 8192)
3784142b486SJames Moore 		max_len = iscsit_text_max_len;
3794142b486SJames Moore #endif
3804142b486SJames Moore 	remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off;
3814142b486SJames Moore 	if (remainder <= max_len) {
3824142b486SJames Moore 		len = remainder;
3834142b486SJames Moore 		final = 1;
3844142b486SJames Moore 	} else {
3854142b486SJames Moore 		len = max_len;
3864142b486SJames Moore 		final = 0;
3874142b486SJames Moore 	}
3884142b486SJames Moore 	/*
3894142b486SJames Moore 	 * Allocate a PDU and copy in text response buffer
3904142b486SJames Moore 	 */
3914142b486SJames Moore 	resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len);
3924142b486SJames Moore 	idm_pdu_init(resp, ict->ict_ic, ict, iscsit_text_resp_complete_cb);
39360220f10SPriya Krishnan 	/* Advance the StatSN for each Text Response sent */
39460220f10SPriya Krishnan 	resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN;
3954142b486SJames Moore 	base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off;
3964142b486SJames Moore 	bcopy(base, resp->isp_data, len);
3974142b486SJames Moore 	/*
3984142b486SJames Moore 	 * Fill in the response header
3994142b486SJames Moore 	 */
4004142b486SJames Moore 	th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr;
4014142b486SJames Moore 	bzero(th_resp, sizeof (*th_resp));
4024142b486SJames Moore 	th_resp->opcode = ISCSI_OP_TEXT_RSP;
4034142b486SJames Moore 	th_resp->itt = th_req->itt;
4044142b486SJames Moore 	hton24(th_resp->dlength, len);
4054142b486SJames Moore 	if (final) {
4064142b486SJames Moore 		th_resp->flags = ISCSI_FLAG_FINAL;
4074142b486SJames Moore 		th_resp->ttt = ISCSI_RSVD_TASK_TAG;
4084142b486SJames Moore 		kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
4094142b486SJames Moore 		ict->ict_text_rsp_buf = NULL;
4104142b486SJames Moore 		ict->ict_text_rsp_len = 0;
4114142b486SJames Moore 		ict->ict_text_rsp_valid_len = 0;
4124142b486SJames Moore 		ict->ict_text_rsp_off = 0;
4134142b486SJames Moore 	} else {
4144142b486SJames Moore 		th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE;
4154142b486SJames Moore 		th_resp->ttt = ict->ict_text_rsp_ttt;
4164142b486SJames Moore 		ict->ict_text_rsp_off += len;
4174142b486SJames Moore 	}
4184142b486SJames Moore 	/* Send the response on its way */
4194142b486SJames Moore 	iscsit_conn_hold(ict);
4204142b486SJames Moore 	iscsit_pdu_tx(resp);
4214142b486SJames Moore 	/* Free the request pdu */
4224142b486SJames Moore 	idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
4234142b486SJames Moore }
4244142b486SJames Moore 
4254142b486SJames Moore /*
4264142b486SJames Moore  * Clean-up the text buffer if it exists.
4274142b486SJames Moore  */
4284142b486SJames Moore void
4294142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict)
4304142b486SJames Moore {
4314142b486SJames Moore 	if (ict->ict_text_rsp_buf != NULL) {
4324142b486SJames Moore 		ASSERT(ict->ict_text_rsp_len != 0);
4334142b486SJames Moore 		kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
4344142b486SJames Moore 	}
4354142b486SJames Moore 	ict->ict_text_rsp_buf = NULL;
4364142b486SJames Moore 	ict->ict_text_rsp_len = 0;
4374142b486SJames Moore 	ict->ict_text_rsp_valid_len = 0;
4384142b486SJames Moore 	ict->ict_text_rsp_off = 0;
4394142b486SJames Moore }
4404142b486SJames Moore 
4414142b486SJames Moore /*
4424142b486SJames Moore  * Process an iSCSI text command.
4434142b486SJames Moore  *
4444142b486SJames Moore  * This code only handles the common case of a text command
4454142b486SJames Moore  * containing the single tuple SendTargets=All issued during
4464142b486SJames Moore  * a discovery session. The request will always arrive in a
4474142b486SJames Moore  * single PDU, but the response may span multiple PDUs if the
4484142b486SJames Moore  * configuration is large. I.e. many targets and portals.
4494142b486SJames Moore  *
4504142b486SJames Moore  * The request is checked for correctness and then the response
4514142b486SJames Moore  * is generated from the global target into nvlist format. Then
4524142b486SJames Moore  * the nvlist is reformatted into idm textbuf format which reflects
4534142b486SJames Moore  * the iSCSI defined <name=value> specification. Finally, the
4544142b486SJames Moore  * textbuf is sent to the initiator in one or more text response PDUs
4554142b486SJames Moore  */
4564142b486SJames Moore void
4574142b486SJames Moore iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
4584142b486SJames Moore {
4594142b486SJames Moore 	iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
460a6d42e7dSPeter Dunlap 	nvlist_t *nv_resp;
4614142b486SJames Moore 	char *kv_pair;
462a6d42e7dSPeter Dunlap 	int flags;
4634142b486SJames Moore 	char *textbuf;
464a6d42e7dSPeter Dunlap 	int textbuflen;
4654142b486SJames Moore 	int validlen;
4664142b486SJames Moore 	iscsit_tgt_t *target, *next_target;
467a6d42e7dSPeter Dunlap 	int rc;
468a6d42e7dSPeter Dunlap 
469a6d42e7dSPeter Dunlap 	flags =  th_req->flags;
470a6d42e7dSPeter Dunlap 	if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) {
4714142b486SJames Moore 		/* Cannot handle multi-PDU requests now */
4724142b486SJames Moore 		iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
473a6d42e7dSPeter Dunlap 		return;
474a6d42e7dSPeter Dunlap 	}
475a6d42e7dSPeter Dunlap 	if (th_req->ttt != ISCSI_RSVD_TASK_TAG) {
4764142b486SJames Moore 		/*
4774142b486SJames Moore 		 * This is the initiator acknowledging our last PDU and
4784142b486SJames Moore 		 * indicating it is ready for the next PDU in the sequence.
4794142b486SJames Moore 		 */
4804142b486SJames Moore 		/*
4814142b486SJames Moore 		 * There can only be one outstanding text request on a
4824142b486SJames Moore 		 * connection. Make sure this one PDU has the current TTT.
4834142b486SJames Moore 		 */
4844142b486SJames Moore 		/* XXX combine the following 3 checks after testing */
4854142b486SJames Moore 		if (th_req->ttt != ict->ict_text_rsp_ttt) {
4864142b486SJames Moore 			/* Not part of this sequence */
4874142b486SJames Moore 			iscsit_text_reject(rx_pdu,
4884142b486SJames Moore 			    ISCSI_REJECT_CMD_NOT_SUPPORTED);
4894142b486SJames Moore 			return;
4904142b486SJames Moore 		}
4914142b486SJames Moore 		/*
4924142b486SJames Moore 		 * ITT should match what was saved from first PDU.
4934142b486SJames Moore 		 */
4944142b486SJames Moore 		if (th_req->itt != ict->ict_text_req_itt) {
4954142b486SJames Moore 			/* Not part of this sequence */
4964142b486SJames Moore 			iscsit_text_reject(rx_pdu,
4974142b486SJames Moore 			    ISCSI_REJECT_CMD_NOT_SUPPORTED);
4984142b486SJames Moore 			return;
4994142b486SJames Moore 		}
5004142b486SJames Moore 		/*
5014142b486SJames Moore 		 * Cannot deal with more key/value pairs now.
5024142b486SJames Moore 		 */
5034142b486SJames Moore 		if (rx_pdu->isp_datalen != 0) {
5044142b486SJames Moore 			iscsit_text_reject(rx_pdu,
5054142b486SJames Moore 			    ISCSI_REJECT_CMD_NOT_SUPPORTED);
5064142b486SJames Moore 			return;
5074142b486SJames Moore 		}
5084142b486SJames Moore 		iscsit_send_next_text_response(ict, rx_pdu);
509a6d42e7dSPeter Dunlap 		return;
510a6d42e7dSPeter Dunlap 	}
511a6d42e7dSPeter Dunlap 
512a6d42e7dSPeter Dunlap 	/*
5134142b486SJames Moore 	 * Initiator has started a new text request. Only
5144142b486SJames Moore 	 * one can be active at a time, so abandon any previous
5154142b486SJames Moore 	 * text request on this connection.
516a6d42e7dSPeter Dunlap 	 */
5174142b486SJames Moore 	iscsit_text_cmd_fini(ict);
5184142b486SJames Moore 
5194142b486SJames Moore 	/* Set the target task tag. */
5204142b486SJames Moore 	iscsit_bump_ttt(ict);
521a6d42e7dSPeter Dunlap 
5224142b486SJames Moore 	/* Save the initiator task tag */
5234142b486SJames Moore 	ict->ict_text_req_itt = th_req->itt;
5244142b486SJames Moore 
5254142b486SJames Moore 	/*
5264142b486SJames Moore 	 * Make sure this is a proper SendTargets request
5274142b486SJames Moore 	 */
528a6d42e7dSPeter Dunlap 	textbuf = (char *)rx_pdu->isp_data;
529a6d42e7dSPeter Dunlap 	textbuflen = rx_pdu->isp_datalen;
530a6d42e7dSPeter Dunlap 	kv_pair = "SendTargets=All";
5314142b486SJames Moore 	if (textbuflen >= strlen(kv_pair) &&
5324142b486SJames Moore 	    strcmp(kv_pair, textbuf) == 0 &&
533a6d42e7dSPeter Dunlap 	    ict->ict_op.op_discovery_session == B_TRUE) {
5348c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		idm_addr_list_t *ipaddr_p;
5358c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		int ipsize;
536a6d42e7dSPeter Dunlap 
537a6d42e7dSPeter Dunlap 		/*
538a6d42e7dSPeter Dunlap 		 * Most common case of SendTargets=All during discovery.
539a6d42e7dSPeter Dunlap 		 */
540a6d42e7dSPeter Dunlap 		/*
541a6d42e7dSPeter Dunlap 		 * Create an nvlist for response.
542a6d42e7dSPeter Dunlap 		 */
543a6d42e7dSPeter Dunlap 		if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) {
5444142b486SJames Moore 			iscsit_text_reject(rx_pdu,
545a6d42e7dSPeter Dunlap 			    ISCSI_REJECT_CMD_NOT_SUPPORTED);
546a6d42e7dSPeter Dunlap 			return;
547a6d42e7dSPeter Dunlap 		}
548a6d42e7dSPeter Dunlap 
5498c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		/*
5508c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		 * get the list of local interface addresses
5518c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		 */
5528c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 
5538c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		ipsize = idm_get_ipaddr(&ipaddr_p);
5548c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 
5554142b486SJames Moore 		/*
5564142b486SJames Moore 		 * Add all the targets to the response list.
5574142b486SJames Moore 		 */
558a6d42e7dSPeter Dunlap 		ISCSIT_GLOBAL_LOCK(RW_READER);
5594142b486SJames Moore 		for (target = avl_first(&iscsit_global.global_target_list);
5604142b486SJames Moore 		    target != NULL;
5614142b486SJames Moore 		    target = next_target) {
5624142b486SJames Moore 			char *key, *value;
5634142b486SJames Moore 			iscsit_tgt_state_t state;
5644142b486SJames Moore 
5654142b486SJames Moore 			next_target = AVL_NEXT(
5664142b486SJames Moore 			    &iscsit_global.global_target_list, target);
5674142b486SJames Moore 
5684142b486SJames Moore 			/* only report online and onlining targets */
5694142b486SJames Moore 			state = target->target_state;
5704142b486SJames Moore 			if (state != TS_ONLINING && state != TS_ONLINE &&
5714142b486SJames Moore 			    state != TS_STMF_ONLINE)
5724142b486SJames Moore 				continue;
5734142b486SJames Moore 
5744142b486SJames Moore 			key = "TargetName";
5754142b486SJames Moore 			value = target->target_name;
5764142b486SJames Moore 			if (nvlist_add_string(nv_resp, key, value) == 0) {
5774142b486SJames Moore 				/* add the portal groups bound to this target */
5788c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 				iscsit_add_tpgs(ict, target, ipaddr_p, nv_resp);
5794142b486SJames Moore 			}
580a6d42e7dSPeter Dunlap 		}
581a6d42e7dSPeter Dunlap 		ISCSIT_GLOBAL_UNLOCK();
5828c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		if (ipsize > 0) {
5838c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 			kmem_free(ipaddr_p, ipsize);
5848c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 		}
585a6d42e7dSPeter Dunlap 
586a6d42e7dSPeter Dunlap 		/*
5874142b486SJames Moore 		 * Convert the response nvlist into an idm text buffer.
588a6d42e7dSPeter Dunlap 		 */
589a6d42e7dSPeter Dunlap 		textbuf = 0;
590a6d42e7dSPeter Dunlap 		textbuflen = 0;
591a6d42e7dSPeter Dunlap 		validlen = 0;
592a6d42e7dSPeter Dunlap 		rc = idm_nvlist_to_textbuf(nv_resp, &textbuf,
593a6d42e7dSPeter Dunlap 		    &textbuflen, &validlen);
594a6d42e7dSPeter Dunlap 		nvlist_free(nv_resp);
595a6d42e7dSPeter Dunlap 		if (rc != 0) {
596a6d42e7dSPeter Dunlap 			if (textbuf && textbuflen)
597a6d42e7dSPeter Dunlap 				kmem_free(textbuf, textbuflen);
5984142b486SJames Moore 			iscsit_text_reject(rx_pdu,
599a6d42e7dSPeter Dunlap 			    ISCSI_REJECT_CMD_NOT_SUPPORTED);
600a6d42e7dSPeter Dunlap 			return;
601a6d42e7dSPeter Dunlap 		}
6024142b486SJames Moore 		ict->ict_text_rsp_buf = textbuf;
6034142b486SJames Moore 		ict->ict_text_rsp_len = textbuflen;
6044142b486SJames Moore 		ict->ict_text_rsp_valid_len = validlen;
6054142b486SJames Moore 		ict->ict_text_rsp_off = 0;
6064142b486SJames Moore 		iscsit_send_next_text_response(ict, rx_pdu);
607a6d42e7dSPeter Dunlap 	} else {
608a6d42e7dSPeter Dunlap 		/*
609a6d42e7dSPeter Dunlap 		 * Other cases to handle
610a6d42e7dSPeter Dunlap 		 *    Discovery session:
611a6d42e7dSPeter Dunlap 		 *	SendTargets=<target_name>
612a6d42e7dSPeter Dunlap 		 *    Normal session
613a6d42e7dSPeter Dunlap 		 *	SendTargets=<NULL> - assume target name of session
614a6d42e7dSPeter Dunlap 		 *    All others
615a6d42e7dSPeter Dunlap 		 *	Error
616a6d42e7dSPeter Dunlap 		 */
6174142b486SJames Moore 		iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
618a6d42e7dSPeter Dunlap 		return;
619a6d42e7dSPeter Dunlap 	}
620a6d42e7dSPeter Dunlap }
621