1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/cpuvar.h> 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/file.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/modctl.h> 33 #include <sys/sysmacros.h> 34 #include <sys/socket.h> 35 #include <sys/strsubr.h> 36 #include <inet/tcp.h> 37 #include <sys/nvpair.h> 38 39 #include <sys/stmf.h> 40 #include <sys/stmf_ioctl.h> 41 #include <sys/portif.h> 42 #include <sys/idm/idm.h> 43 #include <sys/idm/idm_conn_sm.h> 44 #include <sys/idm/idm_text.h> 45 #include <sys/idm/idm_so.h> 46 #include <iscsit_isns.h> 47 #include <iscsit.h> 48 49 #define IPADDRSTRLEN INET6_ADDRSTRLEN /* space for ipaddr string */ 50 #define PORTALSTRLEN (IPADDRSTRLEN+16) /* add space for :port,tag */ 51 52 /* 53 * The kernel inet_ntop() function formats ipv4 address fields with 54 * leading zeros which the win2k initiator interprets as octal. 55 */ 56 57 static void iscsit_v4_ntop(struct in_addr *in, char a[], int size) 58 { 59 unsigned char *p = (unsigned char *) in; 60 61 (void) snprintf(a, size, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3)); 62 } 63 64 static void 65 iscsit_send_reject(idm_pdu_t *req_pdu, uint8_t reason_code) 66 { 67 idm_pdu_t *reject_pdu; 68 iscsi_reject_rsp_hdr_t *rej_hdr; 69 70 reject_pdu = idm_pdu_alloc(sizeof (iscsi_hdr_t), req_pdu->isp_hdrlen); 71 if (reject_pdu == NULL) { 72 /* Just give up.. the initiator will timeout */ 73 idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS); 74 return; 75 } 76 77 /* Payload contains the header from the bad PDU */ 78 idm_pdu_init(reject_pdu, req_pdu->isp_ic, NULL, NULL); 79 bcopy(req_pdu->isp_hdr, reject_pdu->isp_data, req_pdu->isp_hdrlen); 80 81 rej_hdr = (iscsi_reject_rsp_hdr_t *)reject_pdu->isp_hdr; 82 bzero(rej_hdr, sizeof (*rej_hdr)); 83 rej_hdr->opcode = ISCSI_OP_REJECT_MSG; 84 rej_hdr->flags = ISCSI_FLAG_FINAL; 85 rej_hdr->reason = reason_code; 86 hton24(rej_hdr->dlength, req_pdu->isp_hdrlen); 87 rej_hdr->must_be_ff[0] = 0xff; 88 rej_hdr->must_be_ff[1] = 0xff; 89 rej_hdr->must_be_ff[2] = 0xff; 90 rej_hdr->must_be_ff[3] = 0xff; 91 92 iscsit_pdu_tx(reject_pdu); 93 idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS); 94 } 95 96 static void 97 iscsit_add_target_portals(nvlist_t *nv_resp, iscsit_tgt_t *target) 98 { 99 iscsit_tpgt_t *tpg_list; 100 iscsit_tpg_t *tpg; 101 idm_addr_list_t *ipaddr_p; 102 idm_addr_t *tip; 103 iscsit_portal_t *portal; 104 int ipsize, i; 105 char *name = "TargetAddress"; 106 char a[IPADDRSTRLEN]; 107 char v[PORTALSTRLEN]; 108 struct sockaddr_storage *ss; 109 struct sockaddr_in *sin; 110 struct sockaddr_in6 *sin6; 111 struct in_addr *in; 112 struct in6_addr *in6; 113 int type; 114 115 116 /* 117 * Look through the portal groups associated with this target. 118 */ 119 mutex_enter(&target->target_mutex); 120 tpg_list = avl_first(&target->target_tpgt_list); 121 while (tpg_list != NULL) { 122 tpg = tpg_list->tpgt_tpg; 123 /* 124 * The default portal group will match any current interface. 125 * A target cannot listen on other portal groups if it 126 * listens on the default portal group. 127 */ 128 if (tpg == iscsit_global.global_default_tpg) { 129 /* 130 * get the list of plumbed interfaces 131 */ 132 ipsize = idm_get_ipaddr(&ipaddr_p); 133 if (ipsize == 0) { 134 mutex_exit(&target->target_mutex); 135 return; 136 } 137 tip = &ipaddr_p->al_addrs[0]; 138 for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) { 139 if (tip->a_addr.i_insize == 140 sizeof (struct in_addr)) { 141 type = AF_INET; 142 in = &tip->a_addr.i_addr.in4; 143 iscsit_v4_ntop(in, a, sizeof (a)); 144 (void) snprintf(v, sizeof (v), 145 "%s,1", a); 146 } else if (tip->a_addr.i_insize == 147 sizeof (struct in6_addr)) { 148 type = AF_INET6; 149 in6 = &tip->a_addr.i_addr.in6; 150 (void) inet_ntop(type, in6, a, 151 sizeof (a)); 152 (void) snprintf(v, sizeof (v), 153 "[%s],1", a); 154 } else { 155 break; 156 } 157 /* 158 * Add the TargetAddress=<addr> nvpair 159 */ 160 (void) nvlist_add_string(nv_resp, name, v); 161 } 162 kmem_free(ipaddr_p, ipsize); 163 /* 164 * Cannot listen on other portal groups. 165 */ 166 mutex_exit(&target->target_mutex); 167 return; 168 } 169 /* 170 * Found a defined portal group - add each portal address. 171 */ 172 portal = avl_first(&tpg->tpg_portal_list); 173 while (portal != NULL) { 174 ss = &portal->portal_addr; 175 type = ss->ss_family; 176 switch (type) { 177 case AF_INET: 178 sin = (struct sockaddr_in *)ss; 179 in = &sin->sin_addr; 180 iscsit_v4_ntop(in, a, sizeof (a)); 181 (void) snprintf(v, sizeof (v), "%s:%d,%d", a, 182 ntohs(sin->sin_port), 183 tpg_list->tpgt_tag); 184 (void) nvlist_add_string(nv_resp, name, v); 185 break; 186 case AF_INET6: 187 sin6 = (struct sockaddr_in6 *)ss; 188 in6 = &sin6->sin6_addr; 189 (void) inet_ntop(type, in6, a, sizeof (a)); 190 (void) snprintf(v, sizeof (v), "[%s]:%d,%d", a, 191 sin6->sin6_port, 192 tpg_list->tpgt_tag); 193 (void) nvlist_add_string(nv_resp, name, v); 194 break; 195 default: 196 break; 197 } 198 portal = AVL_NEXT(&tpg->tpg_portal_list, portal); 199 } 200 tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list); 201 } 202 mutex_exit(&target->target_mutex); 203 } 204 205 void 206 iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu) 207 { 208 iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr; 209 iscsi_text_rsp_hdr_t *th_resp; 210 nvlist_t *nv_resp; 211 char *textbuf; 212 char *kv_name, *kv_pair; 213 int flags; 214 int textbuflen; 215 int rc; 216 idm_pdu_t *resp; 217 218 flags = th_req->flags; 219 if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) { 220 /* Cannot handle multi-PDU messages now */ 221 iscsit_send_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 222 return; 223 } 224 if (th_req->ttt != ISCSI_RSVD_TASK_TAG) { 225 /* Last of a multi-PDU message */ 226 iscsit_send_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 227 return; 228 } 229 230 /* 231 * At this point we have a single PDU text command 232 */ 233 234 textbuf = (char *)rx_pdu->isp_data; 235 textbuflen = rx_pdu->isp_datalen; 236 kv_name = "SendTargets="; 237 kv_pair = "SendTargets=All"; 238 if (strncmp(kv_name, textbuf, strlen(kv_name)) != 0) { 239 /* Not a Sendtargets command */ 240 iscsit_send_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 241 return; 242 } 243 if (strcmp(kv_pair, textbuf) == 0 && 244 ict->ict_op.op_discovery_session == B_TRUE) { 245 iscsit_tgt_t *target; 246 int validlen; 247 248 /* 249 * Most common case of SendTargets=All during discovery. 250 */ 251 /* 252 * Create an nvlist for response. 253 */ 254 if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) { 255 iscsit_send_reject(rx_pdu, 256 ISCSI_REJECT_CMD_NOT_SUPPORTED); 257 return; 258 } 259 260 ISCSIT_GLOBAL_LOCK(RW_READER); 261 target = avl_first(&iscsit_global.global_target_list); 262 while (target != NULL) { 263 char *name = "TargetName"; 264 char *val = target->target_name; 265 266 (void) nvlist_add_string(nv_resp, name, val); 267 iscsit_add_target_portals(nv_resp, target); 268 target = AVL_NEXT(&iscsit_global.global_target_list, 269 target); 270 } 271 ISCSIT_GLOBAL_UNLOCK(); 272 273 /* 274 * Convert the reponse nv list into text buffer. 275 */ 276 textbuf = 0; 277 textbuflen = 0; 278 validlen = 0; 279 rc = idm_nvlist_to_textbuf(nv_resp, &textbuf, 280 &textbuflen, &validlen); 281 nvlist_free(nv_resp); 282 if (rc != 0) { 283 if (textbuf && textbuflen) 284 kmem_free(textbuf, textbuflen); 285 iscsit_send_reject(rx_pdu, 286 ISCSI_REJECT_CMD_NOT_SUPPORTED); 287 return; 288 } 289 /* 290 * Allocate a PDU and copy in text response buffer 291 */ 292 resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), validlen); 293 idm_pdu_init(resp, ict->ict_ic, NULL, NULL); 294 bcopy(textbuf, resp->isp_data, validlen); 295 kmem_free(textbuf, textbuflen); 296 /* 297 * Fill in the response header 298 */ 299 th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr; 300 bzero(th_resp, sizeof (*th_resp)); 301 th_resp->opcode = ISCSI_OP_TEXT_RSP; 302 th_resp->flags = ISCSI_FLAG_FINAL; 303 th_resp->ttt = ISCSI_RSVD_TASK_TAG; 304 th_resp->itt = th_req->itt; 305 hton24(th_resp->dlength, validlen); 306 } else { 307 /* 308 * Other cases to handle 309 * Discovery session: 310 * SendTargets=<target_name> 311 * Normal session 312 * SendTargets=<target_name> - should match session 313 * SendTargets=<NULL> - assume target name of session 314 * All others 315 * Error 316 */ 317 iscsit_send_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED); 318 return; 319 } 320 321 /* Send the response on its way */ 322 iscsit_pdu_tx(resp); 323 idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS); 324 } 325