iscsit_text.c (7978:4559e57ec313) iscsit_text.c (9601:e0ed15140e6d)
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

--- 5 unchanged lines hidden (view full) ---

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/*
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

--- 5 unchanged lines hidden (view full) ---

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.
22 * Copyright 2009 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>

--- 13 unchanged lines hidden (view full) ---

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
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>

--- 13 unchanged lines hidden (view full) ---

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
52void
53iscsit_text_cmd_fini(iscsit_conn_t *ict);
54
52/*
53 * The kernel inet_ntop() function formats ipv4 address fields with
54 * leading zeros which the win2k initiator interprets as octal.
55 */
56
57static 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
64static void
55/*
56 * The kernel inet_ntop() function formats ipv4 address fields with
57 * leading zeros which the win2k initiator interprets as octal.
58 */
59
60static void iscsit_v4_ntop(struct in_addr *in, char a[], int size)
61{
62 unsigned char *p = (unsigned char *) in;
63
64 (void) snprintf(a, size, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3));
65}
66
67static void
65iscsit_send_reject(idm_pdu_t *req_pdu, uint8_t reason_code)
68iscsit_bump_ttt(iscsit_conn_t *ict)
66{
69{
67 idm_pdu_t *reject_pdu;
68 iscsi_reject_rsp_hdr_t *rej_hdr;
70 /*
71 * Set the target task tag. The value will be zero when
72 * the connection is created. Increment it and wrap it
73 * back to one if we hit the reserved value.
74 *
75 * The TTT is fabricated since there is no real task associated
76 * with a text request. The idm task range is reused here since
77 * no real tasks can be started from a discovery session and
78 * thus no conflicts are possible.
79 */
80 if (++ict->ict_text_rsp_ttt == IDM_TASKIDS_MAX)
81 ict->ict_text_rsp_ttt = 1;
82}
69
83
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;
84static void
85iscsit_text_resp_complete_cb(idm_pdu_t *pdu, idm_status_t status)
86{
87 iscsit_conn_t *ict = pdu->isp_private;
88
89 idm_pdu_free(pdu);
90 if (status != IDM_STATUS_SUCCESS) {
91 /*
92 * Could not send the last text response.
93 * Clear any state and bump the TTT so subsequent
94 * requests will not match.
95 */
96 iscsit_text_cmd_fini(ict);
97 iscsit_bump_ttt(ict);
75 }
98 }
99 iscsit_conn_rele(ict);
100}
76
101
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);
102static void
103iscsit_text_reject(idm_pdu_t *req_pdu, uint8_t reason_code)
104{
105 iscsit_conn_t *ict = req_pdu->isp_ic->ic_handle;
80
106
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);
107 /*
108 * A reject means abandoning this text request.
109 * Cleanup any state from the request and increment the TTT
110 * in case the initiator does not get the reject response
111 * and attempts to resume this request.
112 */
113 iscsit_text_cmd_fini(ict);
114 iscsit_bump_ttt(ict);
115 iscsit_send_reject(ict, req_pdu, reason_code);
93 idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS);
116 idm_pdu_complete(req_pdu, IDM_STATUS_SUCCESS);
117
94}
95
118}
119
120
121/*
122 * Add individual <TargetAddress=ipaddr> tuple to the nvlist
123 */
96static void
124static void
97iscsit_add_target_portals(nvlist_t *nv_resp, iscsit_tgt_t *target)
125iscsit_add_portal(struct sockaddr_storage *ss, int flip_v6, int tag,
126 nvlist_t *nv_resp)
98{
127{
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;
128 char ipaddr[IPADDRSTRLEN]; /* ip address string */
129 char ta_value[PORTALSTRLEN]; /* target address value */
109 struct sockaddr_in *sin;
130 struct sockaddr_in *sin;
110 struct sockaddr_in6 *sin6;
111 struct in_addr *in;
131 struct in_addr *in;
112 struct in6_addr *in6;
113 int type;
132 struct sockaddr_in6 *sin6;
133 struct in6_addr *in6, flip_in6;
114
134
135 switch (ss->ss_family) {
136 case AF_INET:
137 sin = (struct sockaddr_in *)ss;
138 in = &sin->sin_addr;
139 iscsit_v4_ntop(in, ipaddr, sizeof (ipaddr));
140 (void) snprintf(ta_value, sizeof (ta_value), "%s:%d,%d",
141 ipaddr, ntohs(sin->sin_port), tag);
142 break;
143 case AF_INET6:
144 sin6 = (struct sockaddr_in6 *)ss;
145 in6 = &sin6->sin6_addr;
146 if (flip_v6) {
147 uint16_t *v6_field_i = (uint16_t *)in6;
148 uint16_t *v6_field_o = (uint16_t *)&flip_in6;
149 int i;
115
150
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 /*
151 /*
130 * get the list of plumbed interfaces
152 * Ugh. The iSCSI config data is stored in host
153 * order while the addresses retrieved from the
154 * stack come back in network order. inet_ntop
155 * expects network order.
131 */
156 */
132 ipsize = idm_get_ipaddr(&ipaddr_p);
133 if (ipsize == 0) {
134 mutex_exit(&target->target_mutex);
135 return;
157 for (i = 0; i < 8; i++)
158 *v6_field_o++ = htons(*v6_field_i++);
159 in6 = &flip_in6;
160 }
161 (void) inet_ntop(AF_INET6, in6, ipaddr, sizeof (ipaddr));
162 (void) snprintf(ta_value, sizeof (ta_value), "[%s]:%d,%d",
163 ipaddr, ntohs(sin6->sin6_port), tag);
164 break;
165 default:
166 ASSERT(0);
167 return;
168 }
169 (void) nvlist_add_string(nv_resp, "TargetAddress", ta_value);
170}
171
172/*
173 * Process the special case of the default portal group.
174 * Network addresses are obtained from the network stack and
175 * require some reformatting.
176 */
177static void
178iscsit_add_default_portals(iscsit_conn_t *ict, idm_addr_list_t *ipaddr_p,
179 nvlist_t *nv_resp)
180{
181 int pass, i;
182 idm_addr_t *tip;
183 struct sockaddr_storage ss;
184 struct sockaddr_in *sin;
185 struct sockaddr_in6 *sin6;
186
187 /*
188 * If this request was received on one of the portals,
189 * output that portal first. Most initiators will try to
190 * connect on the first portal in the SendTargets response.
191 * For example, this will avoid the confusing situation of a
192 * discovery coming in on an IB interface and the initiator
193 * then doing the normal login on an ethernet interface.
194 */
195 sin = (struct sockaddr_in *)&ss;
196 sin6 = (struct sockaddr_in6 *)&ss;
197 for (pass = 1; pass <= 2; pass++) {
198 tip = &ipaddr_p->al_addrs[0];
199 for (i = 0; i < ipaddr_p->al_out_cnt; i++, tip++) {
200 /* Convert the address into sockaddr_storage format */
201 switch (tip->a_addr.i_insize) {
202 case sizeof (struct in_addr):
203 sin->sin_family = AF_INET;
204 sin->sin_port = htons(ISCSI_LISTEN_PORT);
205 sin->sin_addr = tip->a_addr.i_addr.in4;
206 break;
207 case sizeof (struct in6_addr):
208 sin6->sin6_family = AF_INET6;
209 sin6->sin6_port = htons(ISCSI_LISTEN_PORT);
210 sin6->sin6_addr = tip->a_addr.i_addr.in6;
211 break;
212 default:
213 ASSERT(0);
214 continue;
136 }
215 }
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 }
216 switch (pass) {
217 case 1:
157 /*
218 /*
158 * Add the TargetAddress=<addr> nvpair
219 * On the first pass, skip portals that
220 * do not match the incoming connection.
159 */
221 */
160 (void) nvlist_add_string(nv_resp, name, v);
222 if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
223 B_TRUE) != 0)
224 continue;
225 break;
226 case 2:
227 /*
228 * On the second pass, process the
229 * remaining portals.
230 */
231 if (idm_ss_compare(&ss, &ict->ict_ic->ic_laddr,
232 B_TRUE) == 0)
233 continue;
234 break;
161 }
235 }
162 kmem_free(ipaddr_p, ipsize);
163 /*
236 /*
164 * Cannot listen on other portal groups.
237 * Add portal to the response list.
238 * Do not byte swap v6 address.
239 * By convention, the default portal group tag == 1
165 */
240 */
166 mutex_exit(&target->target_mutex);
167 return;
241 iscsit_add_portal(&ss, 0, 1, nv_resp);
168 }
242 }
169 /*
170 * Found a defined portal group - add each portal address.
171 */
172 portal = avl_first(&tpg->tpg_portal_list);
173 while (portal != NULL) {
243 }
244}
245
246/*
247 * Process a portal group from the configuration database.
248 */
249static void
250iscsit_add_portals(iscsit_conn_t *ict, iscsit_tpgt_t *tpg_list,
251 nvlist_t *nv_resp)
252{
253 int pass;
254 iscsit_portal_t *portal, *next_portal;
255 iscsit_tpg_t *tpg;
256 struct sockaddr_storage *ss;
257
258 /*
259 * As with the default portal group, output the portal used by
260 * the incoming request first.
261 */
262 tpg = tpg_list->tpgt_tpg;
263 for (pass = 1; pass <= 2; pass++) {
264 for (portal = avl_first(&tpg->tpg_portal_list);
265 portal != NULL;
266 portal = next_portal) {
267
268 next_portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
174 ss = &portal->portal_addr;
269 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);
270 switch (pass) {
271 case 1:
272 /*
273 * On the first pass, skip portals that
274 * do not match the incoming connection.
275 */
276 if (idm_ss_compare(ss,
277 &ict->ict_ic->ic_laddr, B_TRUE) != 0)
278 continue;
185 break;
279 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);
280 case 2:
281 /*
282 * On the second pass, process the
283 * remaining portals.
284 */
285 if (idm_ss_compare(ss,
286 &ict->ict_ic->ic_laddr, B_TRUE) == 0)
287 continue;
194 break;
288 break;
195 default:
196 break;
197 }
289 }
198 portal = AVL_NEXT(&tpg->tpg_portal_list, portal);
290 /*
291 * Add portal to the response list.
292 * Need to byte swap v6 address.
293 */
294 iscsit_add_portal(ss, 1, tpg_list->tpgt_tag, nv_resp);
199 }
295 }
296 }
297}
298
299/*
300 * Process all the portal groups bound to a particular target.
301 */
302static void
303iscsit_add_tpgs(iscsit_conn_t *ict, iscsit_tgt_t *target, nvlist_t *nv_resp)
304{
305 iscsit_tpgt_t *tpg_list;
306 idm_addr_list_t *ipaddr_p;
307 int ipsize;
308
309
310 /*
311 * Look through the portal groups associated with this target.
312 */
313 mutex_enter(&target->target_mutex);
314 tpg_list = avl_first(&target->target_tpgt_list);
315
316 /* check for the default portal group */
317 if (tpg_list->tpgt_tpg == iscsit_global.global_default_tpg) {
318 /*
319 * The default portal group is a special case and will
320 * return all reasonable interfaces on this node.
321 *
322 * A target cannot be bound to other portal groups
323 * if it is bound to the default portal group.
324 */
325 ASSERT(AVL_NEXT(&target->target_tpgt_list, tpg_list) == NULL);
326
327 /*
328 * get the list of local interface addresses
329 */
330 ipsize = idm_get_ipaddr(&ipaddr_p);
331 if (ipsize > 0) {
332 /* convert the ip address list to nvlist format */
333 iscsit_add_default_portals(ict, ipaddr_p, nv_resp);
334 kmem_free(ipaddr_p, ipsize);
335 }
336 mutex_exit(&target->target_mutex);
337 return;
338 }
339
340 /*
341 * Not the default portal group - process the user defined tpgs
342 */
343 ASSERT(tpg_list != NULL);
344 while (tpg_list != NULL) {
345
346 ASSERT(tpg_list->tpgt_tpg != iscsit_global.global_default_tpg);
347
348 /*
349 * Found a defined portal group - add each portal address.
350 * As with the default portal group, make 2 passes over
351 * the addresses in order to output the connection
352 * address first.
353 */
354 iscsit_add_portals(ict, tpg_list, nv_resp);
355
200 tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list);
201 }
202 mutex_exit(&target->target_mutex);
203}
204
356 tpg_list = AVL_NEXT(&target->target_tpgt_list, tpg_list);
357 }
358 mutex_exit(&target->target_mutex);
359}
360
205void
206iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
361#ifdef DEBUG
362/*
363 * To test with smaller PDUs in order to force multi-PDU responses,
364 * set this value such that: 0 < test_max_len < 8192
365 */
366uint32_t iscsit_text_max_len = 0;
367#endif
368
369/*
370 * Format a text response PDU from the text buffer and send it.
371 */
372static void
373iscsit_send_next_text_response(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;
374{
375 iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
376 iscsi_text_rsp_hdr_t *th_resp;
377 idm_pdu_t *resp;
378 uint32_t len, remainder, max_len;
379 char *base;
380 int final;
381
382 max_len = ISCSI_DEFAULT_MAX_RECV_SEG_LEN;
383#ifdef DEBUG
384 if (iscsit_text_max_len > 0 && iscsit_text_max_len < 8192)
385 max_len = iscsit_text_max_len;
386#endif
387 remainder = ict->ict_text_rsp_valid_len - ict->ict_text_rsp_off;
388 if (remainder <= max_len) {
389 len = remainder;
390 final = 1;
391 } else {
392 len = max_len;
393 final = 0;
394 }
395 /*
396 * Allocate a PDU and copy in text response buffer
397 */
398 resp = idm_pdu_alloc(sizeof (iscsi_hdr_t), len);
399 idm_pdu_init(resp, ict->ict_ic, ict, iscsit_text_resp_complete_cb);
400 base = ict->ict_text_rsp_buf + ict->ict_text_rsp_off;
401 bcopy(base, resp->isp_data, len);
402 /*
403 * Fill in the response header
404 */
405 th_resp = (iscsi_text_rsp_hdr_t *)resp->isp_hdr;
406 bzero(th_resp, sizeof (*th_resp));
407 th_resp->opcode = ISCSI_OP_TEXT_RSP;
408 th_resp->itt = th_req->itt;
409 hton24(th_resp->dlength, len);
410 if (final) {
411 th_resp->flags = ISCSI_FLAG_FINAL;
412 th_resp->ttt = ISCSI_RSVD_TASK_TAG;
413 kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
414 ict->ict_text_rsp_buf = NULL;
415 ict->ict_text_rsp_len = 0;
416 ict->ict_text_rsp_valid_len = 0;
417 ict->ict_text_rsp_off = 0;
418 } else {
419 th_resp->flags = ISCSI_FLAG_TEXT_CONTINUE;
420 th_resp->ttt = ict->ict_text_rsp_ttt;
421 ict->ict_text_rsp_off += len;
422 }
423 /* Send the response on its way */
424 iscsit_conn_hold(ict);
425 iscsit_pdu_tx(resp);
426 /* Free the request pdu */
427 idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
428}
429
430/*
431 * Clean-up the text buffer if it exists.
432 */
433void
434iscsit_text_cmd_fini(iscsit_conn_t *ict)
435{
436 if (ict->ict_text_rsp_buf != NULL) {
437 ASSERT(ict->ict_text_rsp_len != 0);
438 kmem_free(ict->ict_text_rsp_buf, ict->ict_text_rsp_len);
439 }
440 ict->ict_text_rsp_buf = NULL;
441 ict->ict_text_rsp_len = 0;
442 ict->ict_text_rsp_valid_len = 0;
443 ict->ict_text_rsp_off = 0;
444}
445
446/*
447 * Process an iSCSI text command.
448 *
449 * This code only handles the common case of a text command
450 * containing the single tuple SendTargets=All issued during
451 * a discovery session. The request will always arrive in a
452 * single PDU, but the response may span multiple PDUs if the
453 * configuration is large. I.e. many targets and portals.
454 *
455 * The request is checked for correctness and then the response
456 * is generated from the global target into nvlist format. Then
457 * the nvlist is reformatted into idm textbuf format which reflects
458 * the iSCSI defined <name=value> specification. Finally, the
459 * textbuf is sent to the initiator in one or more text response PDUs
460 */
461void
462iscsit_pdu_op_text_cmd(iscsit_conn_t *ict, idm_pdu_t *rx_pdu)
463{
464 iscsi_text_hdr_t *th_req = (iscsi_text_hdr_t *)rx_pdu->isp_hdr;
210 nvlist_t *nv_resp;
465 nvlist_t *nv_resp;
211 char *textbuf;
212 char *kv_name, *kv_pair;
466 char *kv_pair;
213 int flags;
467 int flags;
468 char *textbuf;
214 int textbuflen;
469 int textbuflen;
470 int validlen;
471 iscsit_tgt_t *target, *next_target;
215 int rc;
472 int rc;
216 idm_pdu_t *resp;
217
218 flags = th_req->flags;
219 if ((flags & ISCSI_FLAG_FINAL) != ISCSI_FLAG_FINAL) {
473
474 flags = th_req->flags;
475 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);
476 /* Cannot handle multi-PDU requests now */
477 iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
222 return;
223 }
224 if (th_req->ttt != ISCSI_RSVD_TASK_TAG) {
478 return;
479 }
480 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);
481 /*
482 * This is the initiator acknowledging our last PDU and
483 * indicating it is ready for the next PDU in the sequence.
484 */
485 /*
486 * There can only be one outstanding text request on a
487 * connection. Make sure this one PDU has the current TTT.
488 */
489 /* XXX combine the following 3 checks after testing */
490 if (th_req->ttt != ict->ict_text_rsp_ttt) {
491 /* Not part of this sequence */
492 iscsit_text_reject(rx_pdu,
493 ISCSI_REJECT_CMD_NOT_SUPPORTED);
494 return;
495 }
496 /*
497 * ITT should match what was saved from first PDU.
498 */
499 if (th_req->itt != ict->ict_text_req_itt) {
500 /* Not part of this sequence */
501 iscsit_text_reject(rx_pdu,
502 ISCSI_REJECT_CMD_NOT_SUPPORTED);
503 return;
504 }
505 /*
506 * Cannot deal with more key/value pairs now.
507 */
508 if (rx_pdu->isp_datalen != 0) {
509 iscsit_text_reject(rx_pdu,
510 ISCSI_REJECT_CMD_NOT_SUPPORTED);
511 return;
512 }
513 iscsit_send_next_text_response(ict, rx_pdu);
227 return;
228 }
229
230 /*
514 return;
515 }
516
517 /*
231 * At this point we have a single PDU text command
518 * Initiator has started a new text request. Only
519 * one can be active at a time, so abandon any previous
520 * text request on this connection.
232 */
521 */
522 iscsit_text_cmd_fini(ict);
233
523
524 /* Set the target task tag. */
525 iscsit_bump_ttt(ict);
526
527 /* Save the initiator task tag */
528 ict->ict_text_req_itt = th_req->itt;
529
530 /*
531 * Make sure this is a proper SendTargets request
532 */
234 textbuf = (char *)rx_pdu->isp_data;
235 textbuflen = rx_pdu->isp_datalen;
533 textbuf = (char *)rx_pdu->isp_data;
534 textbuflen = rx_pdu->isp_datalen;
236 kv_name = "SendTargets=";
237 kv_pair = "SendTargets=All";
535 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 &&
536 if (textbuflen >= strlen(kv_pair) &&
537 strcmp(kv_pair, textbuf) == 0 &&
244 ict->ict_op.op_discovery_session == B_TRUE) {
538 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) {
539
540 /*
541 * Most common case of SendTargets=All during discovery.
542 */
543 /*
544 * Create an nvlist for response.
545 */
546 if (nvlist_alloc(&nv_resp, 0, KM_SLEEP) != 0) {
255 iscsit_send_reject(rx_pdu,
547 iscsit_text_reject(rx_pdu,
256 ISCSI_REJECT_CMD_NOT_SUPPORTED);
257 return;
258 }
259
548 ISCSI_REJECT_CMD_NOT_SUPPORTED);
549 return;
550 }
551
552 /*
553 * Add all the targets to the response list.
554 */
260 ISCSIT_GLOBAL_LOCK(RW_READER);
555 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;
556 for (target = avl_first(&iscsit_global.global_target_list);
557 target != NULL;
558 target = next_target) {
559 char *key, *value;
560 iscsit_tgt_state_t state;
265
561
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);
562 next_target = AVL_NEXT(
563 &iscsit_global.global_target_list, target);
564
565 /* only report online and onlining targets */
566 state = target->target_state;
567 if (state != TS_ONLINING && state != TS_ONLINE &&
568 state != TS_STMF_ONLINE)
569 continue;
570
571 key = "TargetName";
572 value = target->target_name;
573 if (nvlist_add_string(nv_resp, key, value) == 0) {
574 /* add the portal groups bound to this target */
575 iscsit_add_tpgs(ict, target, nv_resp);
576 }
270 }
271 ISCSIT_GLOBAL_UNLOCK();
272
273 /*
577 }
578 ISCSIT_GLOBAL_UNLOCK();
579
580 /*
274 * Convert the reponse nv list into text buffer.
581 * Convert the response nvlist into an idm 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);
582 */
583 textbuf = 0;
584 textbuflen = 0;
585 validlen = 0;
586 rc = idm_nvlist_to_textbuf(nv_resp, &textbuf,
587 &textbuflen, &validlen);
588 nvlist_free(nv_resp);
589 if (rc != 0) {
590 if (textbuf && textbuflen)
591 kmem_free(textbuf, textbuflen);
285 iscsit_send_reject(rx_pdu,
592 iscsit_text_reject(rx_pdu,
286 ISCSI_REJECT_CMD_NOT_SUPPORTED);
287 return;
288 }
593 ISCSI_REJECT_CMD_NOT_SUPPORTED);
594 return;
595 }
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);
596 ict->ict_text_rsp_buf = textbuf;
597 ict->ict_text_rsp_len = textbuflen;
598 ict->ict_text_rsp_valid_len = validlen;
599 ict->ict_text_rsp_off = 0;
600 iscsit_send_next_text_response(ict, rx_pdu);
306 } else {
307 /*
308 * Other cases to handle
309 * Discovery session:
310 * SendTargets=<target_name>
311 * Normal session
601 } else {
602 /*
603 * Other cases to handle
604 * Discovery session:
605 * SendTargets=<target_name>
606 * Normal session
312 * SendTargets=<target_name> - should match session
313 * SendTargets=<NULL> - assume target name of session
314 * All others
315 * Error
316 */
607 * SendTargets=<NULL> - assume target name of session
608 * All others
609 * Error
610 */
317 iscsit_send_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
611 iscsit_text_reject(rx_pdu, ISCSI_REJECT_CMD_NOT_SUPPORTED);
318 return;
319 }
612 return;
613 }
320
321 /* Send the response on its way */
322 iscsit_pdu_tx(resp);
323 idm_pdu_complete(rx_pdu, IDM_STATUS_SUCCESS);
324}
614}