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 /* 224558d122SViswanathan Kannappan * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23a6d42e7dSPeter Dunlap */ 24*dd647b3dSAlexander Stetsenko /* 25*dd647b3dSAlexander Stetsenko * Copyright 2011, Nexenta Systems, Inc. All rights reserved. 26*dd647b3dSAlexander Stetsenko */ 27a6d42e7dSPeter Dunlap 28a6d42e7dSPeter Dunlap #include <sys/cpuvar.h> 29a6d42e7dSPeter Dunlap #include <sys/types.h> 30a6d42e7dSPeter Dunlap #include <sys/conf.h> 31a6d42e7dSPeter Dunlap #include <sys/file.h> 32a6d42e7dSPeter Dunlap #include <sys/ddi.h> 33a6d42e7dSPeter Dunlap #include <sys/sunddi.h> 34a6d42e7dSPeter Dunlap #include <sys/modctl.h> 35a6d42e7dSPeter Dunlap #include <sys/sysmacros.h> 36a6d42e7dSPeter Dunlap #include <sys/socket.h> 37a6d42e7dSPeter Dunlap #include <sys/strsubr.h> 38a6d42e7dSPeter Dunlap #include <inet/tcp.h> 39a6d42e7dSPeter Dunlap #include <sys/nvpair.h> 40a6d42e7dSPeter Dunlap 41a6d42e7dSPeter Dunlap #include <sys/stmf.h> 42a6d42e7dSPeter Dunlap #include <sys/stmf_ioctl.h> 43a6d42e7dSPeter Dunlap #include <sys/portif.h> 44a6d42e7dSPeter Dunlap #include <sys/idm/idm.h> 45a6d42e7dSPeter Dunlap #include <sys/idm/idm_conn_sm.h> 46a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h> 47a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h> 484558d122SViswanathan Kannappan 494558d122SViswanathan Kannappan #include "iscsit_isns.h" 504558d122SViswanathan Kannappan #include "iscsit.h" 51a6d42e7dSPeter Dunlap 52a6d42e7dSPeter Dunlap #define IPADDRSTRLEN INET6_ADDRSTRLEN /* space for ipaddr string */ 53a6d42e7dSPeter Dunlap #define PORTALSTRLEN (IPADDRSTRLEN+16) /* add space for :port,tag */ 54a6d42e7dSPeter Dunlap 554142b486SJames Moore void 564142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict); 574142b486SJames Moore 58a6d42e7dSPeter Dunlap /* 59a6d42e7dSPeter Dunlap * The kernel inet_ntop() function formats ipv4 address fields with 60a6d42e7dSPeter Dunlap * leading zeros which the win2k initiator interprets as octal. 61a6d42e7dSPeter Dunlap */ 62a6d42e7dSPeter Dunlap 63a6d42e7dSPeter Dunlap static void iscsit_v4_ntop(struct in_addr *in, char a[], int size) 64a6d42e7dSPeter Dunlap { 65a6d42e7dSPeter Dunlap unsigned char *p = (unsigned char *) in; 66a6d42e7dSPeter Dunlap 67a6d42e7dSPeter Dunlap (void) snprintf(a, size, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3)); 68a6d42e7dSPeter Dunlap } 69a6d42e7dSPeter Dunlap 70a6d42e7dSPeter Dunlap static void 714142b486SJames Moore iscsit_bump_ttt(iscsit_conn_t *ict) 72a6d42e7dSPeter Dunlap { 734142b486SJames Moore /* 744142b486SJames Moore * Set the target task tag. The value will be zero when 754142b486SJames Moore * the connection is created. Increment it and wrap it 764142b486SJames Moore * back to one if we hit the reserved value. 774142b486SJames Moore * 784142b486SJames Moore * The TTT is fabricated since there is no real task associated 794142b486SJames Moore * with a text request. The idm task range is reused here since 804142b486SJames Moore * no real tasks can be started from a discovery session and 814142b486SJames Moore * thus no conflicts are possible. 824142b486SJames Moore */ 834142b486SJames Moore if (++ict->ict_text_rsp_ttt == IDM_TASKIDS_MAX) 844142b486SJames Moore ict->ict_text_rsp_ttt = 1; 854142b486SJames Moore } 86a6d42e7dSPeter Dunlap 874142b486SJames Moore static void 884142b486SJames Moore iscsit_text_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status) 894142b486SJames Moore { 904142b486SJames Moore iscsit_conn_t *ict = pdu->isp_private; 914142b486SJames Moore 924142b486SJames Moore idm_pdu_free(pdu); 934142b486SJames Moore if (status != IDM_STATUS_SUCCESS) { 944142b486SJames Moore /* 954142b486SJames Moore * Could not send the last text response. 964142b486SJames Moore * Clear any state and bump the TTT so subsequent 974142b486SJames Moore * requests will not match. 984142b486SJames Moore */ 994142b486SJames Moore iscsit_text_cmd_fini(ict); 1004142b486SJames Moore iscsit_bump_ttt(ict); 101a6d42e7dSPeter Dunlap } 1024142b486SJames Moore iscsit_conn_rele(ict); 1034142b486SJames Moore } 104a6d42e7dSPeter Dunlap 1054142b486SJames Moore static void 1064142b486SJames Moore iscsit_text_reject(idm_pdu_t *req_pdu, uint8_t reason_code) 1074142b486SJames Moore { 1084142b486SJames Moore iscsit_conn_t *ict = req_pdu->isp_ic->ic_handle; 1094142b486SJames Moore 1104142b486SJames Moore /* 1114142b486SJames Moore * A reject means abandoning this text request. 1124142b486SJames Moore * Cleanup any state from the request and increment the TTT 1134142b486SJames Moore * in case the initiator does not get the reject response 1144142b486SJames Moore * and attempts to resume this request. 1154142b486SJames Moore */ 1164142b486SJames Moore iscsit_text_cmd_fini(ict); 1174142b486SJames Moore iscsit_bump_ttt(ict); 1184142b486SJames Moore iscsit_send_reject(ict, req_pdu, reason_code); 119a6d42e7dSPeter Dunlap idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS); 1204142b486SJames Moore 121a6d42e7dSPeter Dunlap } 122a6d42e7dSPeter Dunlap 1234142b486SJames Moore 1244142b486SJames Moore /* 1254142b486SJames Moore * Add individual <TargetAddress=ipaddr> tuple to the nvlist 1264142b486SJames Moore */ 127a6d42e7dSPeter Dunlap static void 1284142b486SJames Moore iscsit_add_portal(struct sockaddr_storage *ss, int flip_v6, int tag, 1294142b486SJames Moore nvlist_t *nv_resp) 130a6d42e7dSPeter Dunlap { 1314142b486SJames Moore char ipaddr[IPADDRSTRLEN]; /* ip address string */ 1324142b486SJames Moore char ta_value[PORTALSTRLEN]; /* target address value */ 133a6d42e7dSPeter Dunlap struct sockaddr_in *sin; 134a6d42e7dSPeter Dunlap struct in_addr *in; 1354142b486SJames Moore struct sockaddr_in6 *sin6; 1364142b486SJames Moore struct in6_addr *in6, flip_in6; 137a6d42e7dSPeter Dunlap 1384142b486SJames Moore switch (ss->ss_family) { 1394142b486SJames Moore case AF_INET: 1404142b486SJames Moore sin = (struct sockaddr_in *)ss; 1414142b486SJames Moore in = &sin->sin_addr; 1424142b486SJames Moore iscsit_v4_ntop(in, ipaddr, sizeof (ipaddr)); 1434142b486SJames Moore (void) snprintf(ta_value, sizeof (ta_value), "%s:%d,%d", 1444142b486SJames Moore ipaddr, ntohs(sin->sin_port), tag); 1454142b486SJames Moore break; 1464142b486SJames Moore case AF_INET6: 1474142b486SJames Moore sin6 = (struct sockaddr_in6 *)ss; 1484142b486SJames Moore in6 = &sin6->sin6_addr; 1494142b486SJames Moore if (flip_v6) { 1504142b486SJames Moore uint16_t *v6_field_i = (uint16_t *)in6; 1514142b486SJames Moore uint16_t *v6_field_o = (uint16_t *)&flip_in6; 1524142b486SJames Moore int i; 153a6d42e7dSPeter Dunlap 154a6d42e7dSPeter Dunlap /* 1554142b486SJames Moore * Ugh. The iSCSI config data is stored in host 1564142b486SJames Moore * order while the addresses retrieved from the 1574142b486SJames Moore * stack come back in network order. inet_ntop 1584142b486SJames Moore * expects network order. 159a6d42e7dSPeter Dunlap */ 1604142b486SJames Moore for (i = 0; i < 8; i++) 1614142b486SJames Moore *v6_field_o++ = htons(*v6_field_i++); 1624142b486SJames Moore in6 = &flip_in6; 1634142b486SJames Moore } 1644142b486SJames Moore (void) inet_ntop(AF_INET6, in6, ipaddr, sizeof (ipaddr)); 1654142b486SJames Moore (void) snprintf(ta_value, sizeof (ta_value), "[%s]:%d,%d", 1664142b486SJames Moore ipaddr, ntohs(sin6->sin6_port), tag); 1674142b486SJames Moore break; 1684142b486SJames Moore default: 1694142b486SJames Moore ASSERT(0); 1704142b486SJames Moore return; 1714142b486SJames Moore } 1724142b486SJames Moore (void) nvlist_add_string(nv_resp, "TargetAddress", ta_value); 1734142b486SJames Moore } 1744142b486SJames Moore 1754142b486SJames Moore /* 1764142b486SJames Moore * Process the special case of the default portal group. 1774142b486SJames Moore * Network addresses are obtained from the network stack and 1784142b486SJames Moore * require some reformatting. 1794142b486SJames Moore */ 1804142b486SJames Moore static void 1814142b486SJames Moore iscsit_add_default_portals(iscsit_conn_t *ict, idm_addr_list_t *ipaddr_p, 1824142b486SJames Moore nvlist_t *nv_resp) 1834142b486SJames Moore { 1844142b486SJames Moore int pass, i; 1854142b486SJames Moore idm_addr_t *tip; 1864142b486SJames Moore struct sockaddr_storage ss; 1874142b486SJames Moore struct sockaddr_in *sin; 1884142b486SJames Moore struct sockaddr_in6 *sin6; 1894142b486SJames Moore 1904142b486SJames Moore /* 1914142b486SJames Moore * If this request was received on one of the portals, 1924142b486SJames Moore * output that portal first. Most initiators will try to 1934142b486SJames Moore * connect on the first portal in the SendTargets response. 1944142b486SJames Moore * For example, this will avoid the confusing situation of a 1954142b486SJames Moore * discovery coming in on an IB interface and the initiator 1964142b486SJames Moore * then doing the normal login on an ethernet interface. 1974142b486SJames Moore */ 1984142b486SJames Moore sin = (struct sockaddr_in *)&ss; 1994142b486SJames Moore sin6 = (struct sockaddr_in6 *)&ss; 2004142b486SJames Moore for (pass = 1; pass <= 2; pass++) { 2014142b486SJames Moore tip = &ipaddr_p->al_addrs[0]; 2024142b486SJames Moore for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) { 2034142b486SJames Moore /* Convert the address into sockaddr_storage format */ 2044142b486SJames Moore switch (tip->a_addr.i_insize) { 2054142b486SJames Moore case sizeof (struct in_addr): 2064142b486SJames Moore sin->sin_family = AF_INET; 2074142b486SJames Moore sin->sin_port = htons(ISCSI_LISTEN_PORT); 2084142b486SJames Moore sin->sin_addr = tip->a_addr.i_addr.in4; 2094142b486SJames Moore break; 2104142b486SJames Moore case sizeof (struct in6_addr): 2114142b486SJames Moore sin6->sin6_family = AF_INET6; 2124142b486SJames Moore sin6->sin6_port = htons(ISCSI_LISTEN_PORT); 2134142b486SJames Moore sin6->sin6_addr = tip->a_addr.i_addr.in6; 2144142b486SJames Moore break; 2154142b486SJames Moore default: 2164142b486SJames Moore ASSERT(0); 2174142b486SJames Moore continue; 218a6d42e7dSPeter Dunlap } 2194142b486SJames Moore switch (pass) { 2204142b486SJames Moore case 1: 2214142b486SJames Moore /* 2224142b486SJames Moore * On the first pass, skip portals that 2234142b486SJames Moore * do not match the incoming connection. 2244142b486SJames Moore */ 2254142b486SJames Moore if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr, 226bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) != 0) 2274142b486SJames Moore continue; 2284142b486SJames Moore break; 2294142b486SJames Moore case 2: 230a6d42e7dSPeter Dunlap /* 2314142b486SJames Moore * On the second pass, process the 2324142b486SJames Moore * remaining portals. 233a6d42e7dSPeter Dunlap */ 2344142b486SJames Moore if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr, 235bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) == 0) 2364142b486SJames Moore continue; 2374142b486SJames Moore break; 238a6d42e7dSPeter Dunlap } 239a6d42e7dSPeter Dunlap /* 2404142b486SJames Moore * Add portal to the response list. 2414142b486SJames Moore * Do not byte swap v6 address. 2424142b486SJames Moore * By convention, the default portal group tag == 1 243a6d42e7dSPeter Dunlap */ 2444142b486SJames Moore iscsit_add_portal(&ss, 0, 1, nv_resp); 245a6d42e7dSPeter Dunlap } 2464142b486SJames Moore } 2474142b486SJames Moore } 2484142b486SJames Moore 2494142b486SJames Moore /* 2504142b486SJames Moore * Process a portal group from the configuration database. 2514142b486SJames Moore */ 2524142b486SJames Moore static void 2534142b486SJames Moore iscsit_add_portals(iscsit_conn_t *ict, iscsit_tpgt_t *tpg_list, 2544142b486SJames Moore nvlist_t *nv_resp) 2554142b486SJames Moore { 2564142b486SJames Moore int pass; 2574142b486SJames Moore iscsit_portal_t *portal, *next_portal; 2584142b486SJames Moore iscsit_tpg_t *tpg; 2594142b486SJames Moore struct sockaddr_storage *ss; 2604142b486SJames Moore 2614142b486SJames Moore /* 2624142b486SJames Moore * As with the default portal group, output the portal used by 2634142b486SJames Moore * the incoming request first. 2644142b486SJames Moore */ 2654142b486SJames Moore tpg = tpg_list->tpgt_tpg; 2664142b486SJames Moore for (pass = 1; pass <= 2; pass++) { 2674142b486SJames Moore for (portal = avl_first(&tpg->tpg_portal_list); 2684142b486SJames Moore portal != NULL; 2694142b486SJames Moore portal = next_portal) { 2704142b486SJames Moore 2714142b486SJames Moore next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal); 272a6d42e7dSPeter Dunlap ss = &portal->portal_addr; 2734142b486SJames Moore switch (pass) { 2744142b486SJames Moore case 1: 2754142b486SJames Moore /* 2764142b486SJames Moore * On the first pass, skip portals that 2774142b486SJames Moore * do not match the incoming connection. 2784142b486SJames Moore */ 279bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr, 280bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) != 0) 2814142b486SJames Moore continue; 282a6d42e7dSPeter Dunlap break; 2834142b486SJames Moore case 2: 2844142b486SJames Moore /* 2854142b486SJames Moore * On the second pass, process the 2864142b486SJames Moore * remaining portals. 2874142b486SJames Moore */ 288bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr, 289bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States B_TRUE, B_TRUE) == 0) 2904142b486SJames Moore continue; 291a6d42e7dSPeter Dunlap break; 292a6d42e7dSPeter Dunlap } 2934142b486SJames Moore /* 2944142b486SJames Moore * Add portal to the response list. 2954142b486SJames Moore * Need to byte swap v6 address. 2964142b486SJames Moore */ 2974142b486SJames Moore iscsit_add_portal(ss, 1, tpg_list->tpgt_tag, nv_resp); 2984142b486SJames Moore } 2994142b486SJames Moore } 3004142b486SJames Moore } 3014142b486SJames Moore 3024142b486SJames Moore /* 3034142b486SJames Moore * Process all the portal groups bound to a particular target. 3044142b486SJames Moore */ 3054142b486SJames Moore static void 3068c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target, 3078c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States idm_addr_list_t *ipaddr_p, nvlist_t *nv_resp) 3084142b486SJames Moore { 3094142b486SJames Moore iscsit_tpgt_t *tpg_list; 3104142b486SJames Moore 3114142b486SJames Moore /* 3124142b486SJames Moore * Look through the portal groups associated with this target. 3134142b486SJames Moore */ 3144142b486SJames Moore mutex_enter(&target->target_mutex); 3154142b486SJames Moore tpg_list = avl_first(&target->target_tpgt_list); 3164142b486SJames Moore 3174142b486SJames Moore /* check for the default portal group */ 3184142b486SJames Moore if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) { 3194142b486SJames Moore /* 3204142b486SJames Moore * The default portal group is a special case and will 3214142b486SJames Moore * return all reasonable interfaces on this node. 3224142b486SJames Moore * 3234142b486SJames Moore * A target cannot be bound to other portal groups 3244142b486SJames Moore * if it is bound to the default portal group. 3254142b486SJames Moore */ 3264142b486SJames Moore ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL); 3274142b486SJames Moore 3288c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (ipaddr_p != NULL) { 3294142b486SJames Moore /* convert the ip address list to nvlist format */ 3304142b486SJames Moore iscsit_add_default_portals(ict, ipaddr_p, nv_resp); 331a6d42e7dSPeter Dunlap } 3324142b486SJames Moore mutex_exit(&target->target_mutex); 3334142b486SJames Moore return; 3344142b486SJames Moore } 3354142b486SJames Moore 3364142b486SJames Moore /* 3374142b486SJames Moore * Not the default portal group - process the user defined tpgs 3384142b486SJames Moore */ 3394142b486SJames Moore ASSERT(tpg_list != NULL); 3404142b486SJames Moore while (tpg_list != NULL) { 3414142b486SJames Moore 3424142b486SJames Moore ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg); 3434142b486SJames Moore 3444142b486SJames Moore /* 3454142b486SJames Moore * Found a defined portal group - add each portal address. 3464142b486SJames Moore * As with the default portal group, make 2 passes over 3474142b486SJames Moore * the addresses in order to output the connection 3484142b486SJames Moore * address first. 3494142b486SJames Moore */ 3504142b486SJames Moore iscsit_add_portals(ict, tpg_list, nv_resp); 3514142b486SJames Moore 352a6d42e7dSPeter Dunlap tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list); 353a6d42e7dSPeter Dunlap } 354a6d42e7dSPeter Dunlap mutex_exit(&target->target_mutex); 355a6d42e7dSPeter Dunlap } 356a6d42e7dSPeter Dunlap 3574142b486SJames Moore #ifdef DEBUG 3584142b486SJames Moore /* 3594142b486SJames Moore * To test with smaller PDUs in order to force multi-PDU responses, 3604142b486SJames Moore * set this value such that: 0 < test_max_len < 8192 3614142b486SJames Moore */ 362*dd647b3dSAlexander Stetsenko uint32_t iscsit_text_max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN; 3634142b486SJames Moore #endif 3644142b486SJames Moore 3654142b486SJames Moore /* 3664142b486SJames Moore * Format a text response PDU from the text buffer and send it. 3674142b486SJames Moore */ 3684142b486SJames Moore static void 3694142b486SJames Moore iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu) 370a6d42e7dSPeter Dunlap { 371a6d42e7dSPeter Dunlap iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr; 372a6d42e7dSPeter Dunlap iscsi_text_rsp_hdr_t *th_resp; 3734142b486SJames Moore idm_pdu_t *resp; 3744142b486SJames Moore uint32_t len, remainder, max_len; 375*dd647b3dSAlexander Stetsenko char *base; 376*dd647b3dSAlexander Stetsenko boolean_t final; 3774142b486SJames Moore 3784142b486SJames Moore max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN; 3794142b486SJames Moore #ifdef DEBUG 380*dd647b3dSAlexander Stetsenko if (iscsit_text_max_len > 0 && iscsit_text_max_len < max_len) 3814142b486SJames Moore max_len = iscsit_text_max_len; 3824142b486SJames Moore #endif 383*dd647b3dSAlexander Stetsenko do { 384*dd647b3dSAlexander Stetsenko remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off; 385*dd647b3dSAlexander Stetsenko if (remainder <= max_len) { 386*dd647b3dSAlexander Stetsenko len = remainder; 387*dd647b3dSAlexander Stetsenko final = B_TRUE; 388*dd647b3dSAlexander Stetsenko } else { 389*dd647b3dSAlexander Stetsenko len = max_len; 390*dd647b3dSAlexander Stetsenko final = B_FALSE; 391*dd647b3dSAlexander Stetsenko } 392*dd647b3dSAlexander Stetsenko /* 393*dd647b3dSAlexander Stetsenko * Allocate a PDU and copy in text response buffer 394*dd647b3dSAlexander Stetsenko */ 395*dd647b3dSAlexander Stetsenko resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len); 396*dd647b3dSAlexander Stetsenko idm_pdu_init(resp, ict->ict_ic, ict, 397*dd647b3dSAlexander Stetsenko iscsit_text_resp_complete_cb); 398*dd647b3dSAlexander Stetsenko /* Advance the StatSN for each Text Response sent */ 399*dd647b3dSAlexander Stetsenko resp->isp_flags |= IDM_PDU_SET_STATSN | IDM_PDU_ADVANCE_STATSN; 400*dd647b3dSAlexander Stetsenko base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off; 401*dd647b3dSAlexander Stetsenko bcopy(base, resp->isp_data, len); 402*dd647b3dSAlexander Stetsenko /* 403*dd647b3dSAlexander Stetsenko * Fill in the response header 404*dd647b3dSAlexander Stetsenko */ 405*dd647b3dSAlexander Stetsenko th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr; 406*dd647b3dSAlexander Stetsenko bzero(th_resp, sizeof (*th_resp)); 407*dd647b3dSAlexander Stetsenko th_resp->opcode = ISCSI_OP_TEXT_RSP; 408*dd647b3dSAlexander Stetsenko th_resp->itt = th_req->itt; 409*dd647b3dSAlexander Stetsenko hton24(th_resp->dlength, len); 410*dd647b3dSAlexander Stetsenko if (final) { 411*dd647b3dSAlexander Stetsenko th_resp->flags = ISCSI_FLAG_FINAL; 412*dd647b3dSAlexander Stetsenko th_resp->ttt = ISCSI_RSVD_TASK_TAG; 413*dd647b3dSAlexander Stetsenko kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len); 414*dd647b3dSAlexander Stetsenko ict->ict_text_rsp_buf = NULL; 415*dd647b3dSAlexander Stetsenko ict->ict_text_rsp_len = 0; 416*dd647b3dSAlexander Stetsenko ict->ict_text_rsp_valid_len = 0; 417*dd647b3dSAlexander Stetsenko ict->ict_text_rsp_off = 0; 418*dd647b3dSAlexander Stetsenko } else { 419*dd647b3dSAlexander Stetsenko th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE; 420*dd647b3dSAlexander Stetsenko th_resp->ttt = ict->ict_text_rsp_ttt; 421*dd647b3dSAlexander Stetsenko ict->ict_text_rsp_off += len; 422*dd647b3dSAlexander Stetsenko } 423*dd647b3dSAlexander Stetsenko /* Send the response on its way */ 424*dd647b3dSAlexander Stetsenko iscsit_conn_hold(ict); 425*dd647b3dSAlexander Stetsenko iscsit_pdu_tx(resp); 426*dd647b3dSAlexander Stetsenko } while (!final); 4274142b486SJames Moore /* Free the request pdu */ 4284142b486SJames Moore idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS); 4294142b486SJames Moore } 4304142b486SJames Moore 4314142b486SJames Moore /* 4324142b486SJames Moore * Clean-up the text buffer if it exists. 4334142b486SJames Moore */ 4344142b486SJames Moore void 4354142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict) 4364142b486SJames Moore { 4374142b486SJames Moore if (ict->ict_text_rsp_buf != NULL) { 4384142b486SJames Moore ASSERT(ict->ict_text_rsp_len != 0); 4394142b486SJames Moore kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len); 4404142b486SJames Moore } 4414142b486SJames Moore ict->ict_text_rsp_buf = NULL; 4424142b486SJames Moore ict->ict_text_rsp_len = 0; 4434142b486SJames Moore ict->ict_text_rsp_valid_len = 0; 4444142b486SJames Moore ict->ict_text_rsp_off = 0; 4454142b486SJames Moore } 4464142b486SJames Moore 4474142b486SJames Moore /* 4484142b486SJames Moore * Process an iSCSI text command. 4494142b486SJames Moore * 4504142b486SJames Moore * This code only handles the common case of a text command 4514142b486SJames Moore * containing the single tuple SendTargets=All issued during 4524142b486SJames Moore * a discovery session. The request will always arrive in a 4534142b486SJames Moore * single PDU, but the response may span multiple PDUs if the 4544142b486SJames Moore * configuration is large. I.e. many targets and portals. 4554142b486SJames Moore * 4564142b486SJames Moore * The request is checked for correctness and then the response 4574142b486SJames Moore * is generated from the global target into nvlist format. Then 4584142b486SJames Moore * the nvlist is reformatted into idm textbuf format which reflects 4594142b486SJames Moore * the iSCSI defined <name=value> specification. Finally, the 4604142b486SJames Moore * textbuf is sent to the initiator in one or more text response PDUs 4614142b486SJames Moore */ 4624142b486SJames Moore void 4634142b486SJames Moore iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu) 4644142b486SJames Moore { 4654142b486SJames Moore iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr; 466a6d42e7dSPeter Dunlap nvlist_t *nv_resp; 4674142b486SJames Moore char *kv_pair; 468a6d42e7dSPeter Dunlap int flags; 4694142b486SJames Moore char *textbuf; 470a6d42e7dSPeter Dunlap int textbuflen; 4714142b486SJames Moore int validlen; 4724142b486SJames Moore iscsit_tgt_t *target, *next_target; 473a6d42e7dSPeter Dunlap int rc; 474a6d42e7dSPeter Dunlap 475a6d42e7dSPeter Dunlap flags = th_req->flags; 476a6d42e7dSPeter Dunlap if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) { 4774142b486SJames Moore /* Cannot handle multi-PDU requests now */ 4784142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 479a6d42e7dSPeter Dunlap return; 480a6d42e7dSPeter Dunlap } 481a6d42e7dSPeter Dunlap if (th_req->ttt != ISCSI_RSVD_TASK_TAG) { 4824142b486SJames Moore /* 4834142b486SJames Moore * This is the initiator acknowledging our last PDU and 4844142b486SJames Moore * indicating it is ready for the next PDU in the sequence. 4854142b486SJames Moore */ 4864142b486SJames Moore /* 4874142b486SJames Moore * There can only be one outstanding text request on a 4884142b486SJames Moore * connection. Make sure this one PDU has the current TTT. 4894142b486SJames Moore */ 4904142b486SJames Moore /* XXX combine the following 3 checks after testing */ 4914142b486SJames Moore if (th_req->ttt != ict->ict_text_rsp_ttt) { 4924142b486SJames Moore /* Not part of this sequence */ 4934142b486SJames Moore iscsit_text_reject(rx_pdu, 4944142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 4954142b486SJames Moore return; 4964142b486SJames Moore } 4974142b486SJames Moore /* 4984142b486SJames Moore * ITT should match what was saved from first PDU. 4994142b486SJames Moore */ 5004142b486SJames Moore if (th_req->itt != ict->ict_text_req_itt) { 5014142b486SJames Moore /* Not part of this sequence */ 5024142b486SJames Moore iscsit_text_reject(rx_pdu, 5034142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 5044142b486SJames Moore return; 5054142b486SJames Moore } 5064142b486SJames Moore /* 5074142b486SJames Moore * Cannot deal with more key/value pairs now. 5084142b486SJames Moore */ 5094142b486SJames Moore if (rx_pdu->isp_datalen != 0) { 5104142b486SJames Moore iscsit_text_reject(rx_pdu, 5114142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 5124142b486SJames Moore return; 5134142b486SJames Moore } 5144142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu); 515a6d42e7dSPeter Dunlap return; 516a6d42e7dSPeter Dunlap } 517a6d42e7dSPeter Dunlap 518a6d42e7dSPeter Dunlap /* 5194142b486SJames Moore * Initiator has started a new text request. Only 5204142b486SJames Moore * one can be active at a time, so abandon any previous 5214142b486SJames Moore * text request on this connection. 522a6d42e7dSPeter Dunlap */ 5234142b486SJames Moore iscsit_text_cmd_fini(ict); 5244142b486SJames Moore 5254142b486SJames Moore /* Set the target task tag. */ 5264142b486SJames Moore iscsit_bump_ttt(ict); 527a6d42e7dSPeter Dunlap 5284142b486SJames Moore /* Save the initiator task tag */ 5294142b486SJames Moore ict->ict_text_req_itt = th_req->itt; 5304142b486SJames Moore 5314142b486SJames Moore /* 5324142b486SJames Moore * Make sure this is a proper SendTargets request 5334142b486SJames Moore */ 534a6d42e7dSPeter Dunlap textbuf = (char *)rx_pdu->isp_data; 535a6d42e7dSPeter Dunlap textbuflen = rx_pdu->isp_datalen; 536a6d42e7dSPeter Dunlap kv_pair = "SendTargets=All"; 5374142b486SJames Moore if (textbuflen >= strlen(kv_pair) && 5384142b486SJames Moore strcmp(kv_pair, textbuf) == 0 && 539a6d42e7dSPeter Dunlap ict->ict_op.op_discovery_session == B_TRUE) { 5408c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States idm_addr_list_t *ipaddr_p; 5418c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States int ipsize; 542a6d42e7dSPeter Dunlap 543a6d42e7dSPeter Dunlap /* 544a6d42e7dSPeter Dunlap * Most common case of SendTargets=All during discovery. 545a6d42e7dSPeter Dunlap */ 546a6d42e7dSPeter Dunlap /* 547a6d42e7dSPeter Dunlap * Create an nvlist for response. 548a6d42e7dSPeter Dunlap */ 549a6d42e7dSPeter Dunlap if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) { 5504142b486SJames Moore iscsit_text_reject(rx_pdu, 551a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED); 552a6d42e7dSPeter Dunlap return; 553a6d42e7dSPeter Dunlap } 554a6d42e7dSPeter Dunlap 5558c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States /* 5568c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States * get the list of local interface addresses 5578c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States */ 5588c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 5598c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States ipsize = idm_get_ipaddr(&ipaddr_p); 5608c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States 5614142b486SJames Moore /* 5624142b486SJames Moore * Add all the targets to the response list. 5634142b486SJames Moore */ 564a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_LOCK(RW_READER); 5654142b486SJames Moore for (target = avl_first(&iscsit_global.global_target_list); 5664142b486SJames Moore target != NULL; 5674142b486SJames Moore target = next_target) { 5684142b486SJames Moore char *key, *value; 5694142b486SJames Moore iscsit_tgt_state_t state; 5704142b486SJames Moore 5714142b486SJames Moore next_target = AVL_NEXT( 5724142b486SJames Moore &iscsit_global.global_target_list, target); 5734142b486SJames Moore 5744142b486SJames Moore /* only report online and onlining targets */ 5754142b486SJames Moore state = target->target_state; 5764142b486SJames Moore if (state != TS_ONLINING && state != TS_ONLINE && 5774142b486SJames Moore state != TS_STMF_ONLINE) 5784142b486SJames Moore continue; 5794142b486SJames Moore 5804142b486SJames Moore key = "TargetName"; 5814142b486SJames Moore value = target->target_name; 5824142b486SJames Moore if (nvlist_add_string(nv_resp, key, value) == 0) { 5834142b486SJames Moore /* add the portal groups bound to this target */ 5848c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States iscsit_add_tpgs(ict, target, ipaddr_p, nv_resp); 5854142b486SJames Moore } 586a6d42e7dSPeter Dunlap } 587a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_UNLOCK(); 5888c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (ipsize > 0) { 5898c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States kmem_free(ipaddr_p, ipsize); 5908c629652SPeter Cudhea - Sun Microsystems - Burlington, MA United States } 591a6d42e7dSPeter Dunlap 592a6d42e7dSPeter Dunlap /* 5934142b486SJames Moore * Convert the response nvlist into an idm text buffer. 594a6d42e7dSPeter Dunlap */ 595a6d42e7dSPeter Dunlap textbuf = 0; 596a6d42e7dSPeter Dunlap textbuflen = 0; 597a6d42e7dSPeter Dunlap validlen = 0; 598a6d42e7dSPeter Dunlap rc = idm_nvlist_to_textbuf(nv_resp, &textbuf, 599a6d42e7dSPeter Dunlap &textbuflen, &validlen); 600a6d42e7dSPeter Dunlap nvlist_free(nv_resp); 601a6d42e7dSPeter Dunlap if (rc != 0) { 602a6d42e7dSPeter Dunlap if (textbuf && textbuflen) 603a6d42e7dSPeter Dunlap kmem_free(textbuf, textbuflen); 6044142b486SJames Moore iscsit_text_reject(rx_pdu, 605a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED); 606a6d42e7dSPeter Dunlap return; 607a6d42e7dSPeter Dunlap } 6084142b486SJames Moore ict->ict_text_rsp_buf = textbuf; 6094142b486SJames Moore ict->ict_text_rsp_len = textbuflen; 6104142b486SJames Moore ict->ict_text_rsp_valid_len = validlen; 6114142b486SJames Moore ict->ict_text_rsp_off = 0; 6124142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu); 613a6d42e7dSPeter Dunlap } else { 614a6d42e7dSPeter Dunlap /* 615a6d42e7dSPeter Dunlap * Other cases to handle 616a6d42e7dSPeter Dunlap * Discovery session: 617a6d42e7dSPeter Dunlap * SendTargets=<target_name> 618a6d42e7dSPeter Dunlap * Normal session 619a6d42e7dSPeter Dunlap * SendTargets=<NULL> - assume target name of session 620a6d42e7dSPeter Dunlap * All others 621a6d42e7dSPeter Dunlap * Error 622a6d42e7dSPeter Dunlap */ 6234142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 624a6d42e7dSPeter Dunlap return; 625a6d42e7dSPeter Dunlap } 626a6d42e7dSPeter Dunlap } 627