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 /* 224142b486SJames Moore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23a6d42e7dSPeter Dunlap * Use is subject to license terms. 24a6d42e7dSPeter Dunlap */ 25a6d42e7dSPeter Dunlap 26a6d42e7dSPeter Dunlap #include <sys/cpuvar.h> 27a6d42e7dSPeter Dunlap #include <sys/types.h> 28a6d42e7dSPeter Dunlap #include <sys/conf.h> 29a6d42e7dSPeter Dunlap #include <sys/file.h> 30a6d42e7dSPeter Dunlap #include <sys/ddi.h> 31a6d42e7dSPeter Dunlap #include <sys/sunddi.h> 32a6d42e7dSPeter Dunlap #include <sys/modctl.h> 33a6d42e7dSPeter Dunlap #include <sys/sysmacros.h> 34a6d42e7dSPeter Dunlap #include <sys/socket.h> 35a6d42e7dSPeter Dunlap #include <sys/strsubr.h> 36a6d42e7dSPeter Dunlap #include <inet/tcp.h> 37a6d42e7dSPeter Dunlap #include <sys/nvpair.h> 38a6d42e7dSPeter Dunlap 39a6d42e7dSPeter Dunlap #include <sys/stmf.h> 40a6d42e7dSPeter Dunlap #include <sys/stmf_ioctl.h> 41a6d42e7dSPeter Dunlap #include <sys/portif.h> 42a6d42e7dSPeter Dunlap #include <sys/idm/idm.h> 43a6d42e7dSPeter Dunlap #include <sys/idm/idm_conn_sm.h> 44a6d42e7dSPeter Dunlap #include <sys/idm/idm_text.h> 45a6d42e7dSPeter Dunlap #include <sys/idm/idm_so.h> 46a6d42e7dSPeter Dunlap #include <iscsit_isns.h> 47a6d42e7dSPeter Dunlap #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, 223*bdbe8dc6SPeter 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, 232*bdbe8dc6SPeter 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 */ 276*bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr, 277*bdbe8dc6SPeter 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 */ 285*bdbe8dc6SPeter Cudhea - Sun Microsystems - Burlington, MA United States if (idm_ss_compare(ss, &ict->ict_ic->ic_laddr, 286*bdbe8dc6SPeter 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 3034142b486SJames Moore iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target, nvlist_t *nv_resp) 3044142b486SJames Moore { 3054142b486SJames Moore iscsit_tpgt_t *tpg_list; 3064142b486SJames Moore idm_addr_list_t *ipaddr_p; 3074142b486SJames Moore int ipsize; 3084142b486SJames Moore 3094142b486SJames Moore 3104142b486SJames Moore /* 3114142b486SJames Moore * Look through the portal groups associated with this target. 3124142b486SJames Moore */ 3134142b486SJames Moore mutex_enter(&target->target_mutex); 3144142b486SJames Moore tpg_list = avl_first(&target->target_tpgt_list); 3154142b486SJames Moore 3164142b486SJames Moore /* check for the default portal group */ 3174142b486SJames Moore if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) { 3184142b486SJames Moore /* 3194142b486SJames Moore * The default portal group is a special case and will 3204142b486SJames Moore * return all reasonable interfaces on this node. 3214142b486SJames Moore * 3224142b486SJames Moore * A target cannot be bound to other portal groups 3234142b486SJames Moore * if it is bound to the default portal group. 3244142b486SJames Moore */ 3254142b486SJames Moore ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL); 3264142b486SJames Moore 3274142b486SJames Moore /* 3284142b486SJames Moore * get the list of local interface addresses 3294142b486SJames Moore */ 3304142b486SJames Moore ipsize = idm_get_ipaddr(&ipaddr_p); 3314142b486SJames Moore if (ipsize > 0) { 3324142b486SJames Moore /* convert the ip address list to nvlist format */ 3334142b486SJames Moore iscsit_add_default_portals(ict, ipaddr_p, nv_resp); 3344142b486SJames Moore kmem_free(ipaddr_p, ipsize); 335a6d42e7dSPeter Dunlap } 3364142b486SJames Moore mutex_exit(&target->target_mutex); 3374142b486SJames Moore return; 3384142b486SJames Moore } 3394142b486SJames Moore 3404142b486SJames Moore /* 3414142b486SJames Moore * Not the default portal group - process the user defined tpgs 3424142b486SJames Moore */ 3434142b486SJames Moore ASSERT(tpg_list != NULL); 3444142b486SJames Moore while (tpg_list != NULL) { 3454142b486SJames Moore 3464142b486SJames Moore ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg); 3474142b486SJames Moore 3484142b486SJames Moore /* 3494142b486SJames Moore * Found a defined portal group - add each portal address. 3504142b486SJames Moore * As with the default portal group, make 2 passes over 3514142b486SJames Moore * the addresses in order to output the connection 3524142b486SJames Moore * address first. 3534142b486SJames Moore */ 3544142b486SJames Moore iscsit_add_portals(ict, tpg_list, nv_resp); 3554142b486SJames Moore 356a6d42e7dSPeter Dunlap tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list); 357a6d42e7dSPeter Dunlap } 358a6d42e7dSPeter Dunlap mutex_exit(&target->target_mutex); 359a6d42e7dSPeter Dunlap } 360a6d42e7dSPeter Dunlap 3614142b486SJames Moore #ifdef DEBUG 3624142b486SJames Moore /* 3634142b486SJames Moore * To test with smaller PDUs in order to force multi-PDU responses, 3644142b486SJames Moore * set this value such that: 0 < test_max_len < 8192 3654142b486SJames Moore */ 3664142b486SJames Moore uint32_t iscsit_text_max_len = 0; 3674142b486SJames Moore #endif 3684142b486SJames Moore 3694142b486SJames Moore /* 3704142b486SJames Moore * Format a text response PDU from the text buffer and send it. 3714142b486SJames Moore */ 3724142b486SJames Moore static void 3734142b486SJames Moore iscsit_send_next_text_response(iscsit_conn_t *ict, idm_pdu_t *rx_pdu) 374a6d42e7dSPeter Dunlap { 375a6d42e7dSPeter Dunlap iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr; 376a6d42e7dSPeter Dunlap iscsi_text_rsp_hdr_t *th_resp; 3774142b486SJames Moore idm_pdu_t *resp; 3784142b486SJames Moore uint32_t len, remainder, max_len; 3794142b486SJames Moore char *base; 3804142b486SJames Moore int final; 3814142b486SJames Moore 3824142b486SJames Moore max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN; 3834142b486SJames Moore #ifdef DEBUG 3844142b486SJames Moore if (iscsit_text_max_len > 0 && iscsit_text_max_len < 8192) 3854142b486SJames Moore max_len = iscsit_text_max_len; 3864142b486SJames Moore #endif 3874142b486SJames Moore remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off; 3884142b486SJames Moore if (remainder <= max_len) { 3894142b486SJames Moore len = remainder; 3904142b486SJames Moore final = 1; 3914142b486SJames Moore } else { 3924142b486SJames Moore len = max_len; 3934142b486SJames Moore final = 0; 3944142b486SJames Moore } 3954142b486SJames Moore /* 3964142b486SJames Moore * Allocate a PDU and copy in text response buffer 3974142b486SJames Moore */ 3984142b486SJames Moore resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len); 3994142b486SJames Moore idm_pdu_init(resp, ict->ict_ic, ict, iscsit_text_resp_complete_cb); 4004142b486SJames Moore base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off; 4014142b486SJames Moore bcopy(base, resp->isp_data, len); 4024142b486SJames Moore /* 4034142b486SJames Moore * Fill in the response header 4044142b486SJames Moore */ 4054142b486SJames Moore th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr; 4064142b486SJames Moore bzero(th_resp, sizeof (*th_resp)); 4074142b486SJames Moore th_resp->opcode = ISCSI_OP_TEXT_RSP; 4084142b486SJames Moore th_resp->itt = th_req->itt; 4094142b486SJames Moore hton24(th_resp->dlength, len); 4104142b486SJames Moore if (final) { 4114142b486SJames Moore th_resp->flags = ISCSI_FLAG_FINAL; 4124142b486SJames Moore th_resp->ttt = ISCSI_RSVD_TASK_TAG; 4134142b486SJames Moore kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len); 4144142b486SJames Moore ict->ict_text_rsp_buf = NULL; 4154142b486SJames Moore ict->ict_text_rsp_len = 0; 4164142b486SJames Moore ict->ict_text_rsp_valid_len = 0; 4174142b486SJames Moore ict->ict_text_rsp_off = 0; 4184142b486SJames Moore } else { 4194142b486SJames Moore th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE; 4204142b486SJames Moore th_resp->ttt = ict->ict_text_rsp_ttt; 4214142b486SJames Moore ict->ict_text_rsp_off += len; 4224142b486SJames Moore } 4234142b486SJames Moore /* Send the response on its way */ 4244142b486SJames Moore iscsit_conn_hold(ict); 4254142b486SJames Moore iscsit_pdu_tx(resp); 4264142b486SJames Moore /* Free the request pdu */ 4274142b486SJames Moore idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS); 4284142b486SJames Moore } 4294142b486SJames Moore 4304142b486SJames Moore /* 4314142b486SJames Moore * Clean-up the text buffer if it exists. 4324142b486SJames Moore */ 4334142b486SJames Moore void 4344142b486SJames Moore iscsit_text_cmd_fini(iscsit_conn_t *ict) 4354142b486SJames Moore { 4364142b486SJames Moore if (ict->ict_text_rsp_buf != NULL) { 4374142b486SJames Moore ASSERT(ict->ict_text_rsp_len != 0); 4384142b486SJames Moore kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len); 4394142b486SJames Moore } 4404142b486SJames Moore ict->ict_text_rsp_buf = NULL; 4414142b486SJames Moore ict->ict_text_rsp_len = 0; 4424142b486SJames Moore ict->ict_text_rsp_valid_len = 0; 4434142b486SJames Moore ict->ict_text_rsp_off = 0; 4444142b486SJames Moore } 4454142b486SJames Moore 4464142b486SJames Moore /* 4474142b486SJames Moore * Process an iSCSI text command. 4484142b486SJames Moore * 4494142b486SJames Moore * This code only handles the common case of a text command 4504142b486SJames Moore * containing the single tuple SendTargets=All issued during 4514142b486SJames Moore * a discovery session. The request will always arrive in a 4524142b486SJames Moore * single PDU, but the response may span multiple PDUs if the 4534142b486SJames Moore * configuration is large. I.e. many targets and portals. 4544142b486SJames Moore * 4554142b486SJames Moore * The request is checked for correctness and then the response 4564142b486SJames Moore * is generated from the global target into nvlist format. Then 4574142b486SJames Moore * the nvlist is reformatted into idm textbuf format which reflects 4584142b486SJames Moore * the iSCSI defined <name=value> specification. Finally, the 4594142b486SJames Moore * textbuf is sent to the initiator in one or more text response PDUs 4604142b486SJames Moore */ 4614142b486SJames Moore void 4624142b486SJames Moore iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu) 4634142b486SJames Moore { 4644142b486SJames Moore iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr; 465a6d42e7dSPeter Dunlap nvlist_t *nv_resp; 4664142b486SJames Moore char *kv_pair; 467a6d42e7dSPeter Dunlap int flags; 4684142b486SJames Moore char *textbuf; 469a6d42e7dSPeter Dunlap int textbuflen; 4704142b486SJames Moore int validlen; 4714142b486SJames Moore iscsit_tgt_t *target, *next_target; 472a6d42e7dSPeter Dunlap int rc; 473a6d42e7dSPeter Dunlap 474a6d42e7dSPeter Dunlap flags = th_req->flags; 475a6d42e7dSPeter Dunlap if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) { 4764142b486SJames Moore /* Cannot handle multi-PDU requests now */ 4774142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 478a6d42e7dSPeter Dunlap return; 479a6d42e7dSPeter Dunlap } 480a6d42e7dSPeter Dunlap if (th_req->ttt != ISCSI_RSVD_TASK_TAG) { 4814142b486SJames Moore /* 4824142b486SJames Moore * This is the initiator acknowledging our last PDU and 4834142b486SJames Moore * indicating it is ready for the next PDU in the sequence. 4844142b486SJames Moore */ 4854142b486SJames Moore /* 4864142b486SJames Moore * There can only be one outstanding text request on a 4874142b486SJames Moore * connection. Make sure this one PDU has the current TTT. 4884142b486SJames Moore */ 4894142b486SJames Moore /* XXX combine the following 3 checks after testing */ 4904142b486SJames Moore if (th_req->ttt != ict->ict_text_rsp_ttt) { 4914142b486SJames Moore /* Not part of this sequence */ 4924142b486SJames Moore iscsit_text_reject(rx_pdu, 4934142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 4944142b486SJames Moore return; 4954142b486SJames Moore } 4964142b486SJames Moore /* 4974142b486SJames Moore * ITT should match what was saved from first PDU. 4984142b486SJames Moore */ 4994142b486SJames Moore if (th_req->itt != ict->ict_text_req_itt) { 5004142b486SJames Moore /* Not part of this sequence */ 5014142b486SJames Moore iscsit_text_reject(rx_pdu, 5024142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 5034142b486SJames Moore return; 5044142b486SJames Moore } 5054142b486SJames Moore /* 5064142b486SJames Moore * Cannot deal with more key/value pairs now. 5074142b486SJames Moore */ 5084142b486SJames Moore if (rx_pdu->isp_datalen != 0) { 5094142b486SJames Moore iscsit_text_reject(rx_pdu, 5104142b486SJames Moore ISCSI_REJECT_CMD_NOT_SUPPORTED); 5114142b486SJames Moore return; 5124142b486SJames Moore } 5134142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu); 514a6d42e7dSPeter Dunlap return; 515a6d42e7dSPeter Dunlap } 516a6d42e7dSPeter Dunlap 517a6d42e7dSPeter Dunlap /* 5184142b486SJames Moore * Initiator has started a new text request. Only 5194142b486SJames Moore * one can be active at a time, so abandon any previous 5204142b486SJames Moore * text request on this connection. 521a6d42e7dSPeter Dunlap */ 5224142b486SJames Moore iscsit_text_cmd_fini(ict); 5234142b486SJames Moore 5244142b486SJames Moore /* Set the target task tag. */ 5254142b486SJames Moore iscsit_bump_ttt(ict); 526a6d42e7dSPeter Dunlap 5274142b486SJames Moore /* Save the initiator task tag */ 5284142b486SJames Moore ict->ict_text_req_itt = th_req->itt; 5294142b486SJames Moore 5304142b486SJames Moore /* 5314142b486SJames Moore * Make sure this is a proper SendTargets request 5324142b486SJames Moore */ 533a6d42e7dSPeter Dunlap textbuf = (char *)rx_pdu->isp_data; 534a6d42e7dSPeter Dunlap textbuflen = rx_pdu->isp_datalen; 535a6d42e7dSPeter Dunlap kv_pair = "SendTargets=All"; 5364142b486SJames Moore if (textbuflen >= strlen(kv_pair) && 5374142b486SJames Moore strcmp(kv_pair, textbuf) == 0 && 538a6d42e7dSPeter Dunlap ict->ict_op.op_discovery_session == B_TRUE) { 539a6d42e7dSPeter Dunlap 540a6d42e7dSPeter Dunlap /* 541a6d42e7dSPeter Dunlap * Most common case of SendTargets=All during discovery. 542a6d42e7dSPeter Dunlap */ 543a6d42e7dSPeter Dunlap /* 544a6d42e7dSPeter Dunlap * Create an nvlist for response. 545a6d42e7dSPeter Dunlap */ 546a6d42e7dSPeter Dunlap if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) { 5474142b486SJames Moore iscsit_text_reject(rx_pdu, 548a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED); 549a6d42e7dSPeter Dunlap return; 550a6d42e7dSPeter Dunlap } 551a6d42e7dSPeter Dunlap 5524142b486SJames Moore /* 5534142b486SJames Moore * Add all the targets to the response list. 5544142b486SJames Moore */ 555a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_LOCK(RW_READER); 5564142b486SJames Moore for (target = avl_first(&iscsit_global.global_target_list); 5574142b486SJames Moore target != NULL; 5584142b486SJames Moore target = next_target) { 5594142b486SJames Moore char *key, *value; 5604142b486SJames Moore iscsit_tgt_state_t state; 5614142b486SJames Moore 5624142b486SJames Moore next_target = AVL_NEXT( 5634142b486SJames Moore &iscsit_global.global_target_list, target); 5644142b486SJames Moore 5654142b486SJames Moore /* only report online and onlining targets */ 5664142b486SJames Moore state = target->target_state; 5674142b486SJames Moore if (state != TS_ONLINING && state != TS_ONLINE && 5684142b486SJames Moore state != TS_STMF_ONLINE) 5694142b486SJames Moore continue; 5704142b486SJames Moore 5714142b486SJames Moore key = "TargetName"; 5724142b486SJames Moore value = target->target_name; 5734142b486SJames Moore if (nvlist_add_string(nv_resp, key, value) == 0) { 5744142b486SJames Moore /* add the portal groups bound to this target */ 5754142b486SJames Moore iscsit_add_tpgs(ict, target, nv_resp); 5764142b486SJames Moore } 577a6d42e7dSPeter Dunlap } 578a6d42e7dSPeter Dunlap ISCSIT_GLOBAL_UNLOCK(); 579a6d42e7dSPeter Dunlap 580a6d42e7dSPeter Dunlap /* 5814142b486SJames Moore * Convert the response nvlist into an idm text buffer. 582a6d42e7dSPeter Dunlap */ 583a6d42e7dSPeter Dunlap textbuf = 0; 584a6d42e7dSPeter Dunlap textbuflen = 0; 585a6d42e7dSPeter Dunlap validlen = 0; 586a6d42e7dSPeter Dunlap rc = idm_nvlist_to_textbuf(nv_resp, &textbuf, 587a6d42e7dSPeter Dunlap &textbuflen, &validlen); 588a6d42e7dSPeter Dunlap nvlist_free(nv_resp); 589a6d42e7dSPeter Dunlap if (rc != 0) { 590a6d42e7dSPeter Dunlap if (textbuf && textbuflen) 591a6d42e7dSPeter Dunlap kmem_free(textbuf, textbuflen); 5924142b486SJames Moore iscsit_text_reject(rx_pdu, 593a6d42e7dSPeter Dunlap ISCSI_REJECT_CMD_NOT_SUPPORTED); 594a6d42e7dSPeter Dunlap return; 595a6d42e7dSPeter Dunlap } 5964142b486SJames Moore ict->ict_text_rsp_buf = textbuf; 5974142b486SJames Moore ict->ict_text_rsp_len = textbuflen; 5984142b486SJames Moore ict->ict_text_rsp_valid_len = validlen; 5994142b486SJames Moore ict->ict_text_rsp_off = 0; 6004142b486SJames Moore iscsit_send_next_text_response(ict, rx_pdu); 601a6d42e7dSPeter Dunlap } else { 602a6d42e7dSPeter Dunlap /* 603a6d42e7dSPeter Dunlap * Other cases to handle 604a6d42e7dSPeter Dunlap * Discovery session: 605a6d42e7dSPeter Dunlap * SendTargets=<target_name> 606a6d42e7dSPeter Dunlap * Normal session 607a6d42e7dSPeter Dunlap * SendTargets=<NULL> - assume target name of session 608a6d42e7dSPeter Dunlap * All others 609a6d42e7dSPeter Dunlap * Error 610a6d42e7dSPeter Dunlap */ 6114142b486SJames Moore iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 612a6d42e7dSPeter Dunlap return; 613a6d42e7dSPeter Dunlap } 614a6d42e7dSPeter Dunlap } 615