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