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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * iSCSI Software Initiator
26 */
27
28 #include <sys/socket.h> /* networking stuff */
29 #include <sys/strsubr.h> /* networking stuff */
30 #include <netinet/tcp.h> /* TCP_NODELAY */
31 #include <sys/socketvar.h> /* _ALLOC_SLEEP */
32 #include <sys/pathname.h> /* declares: lookupname */
33 #include <sys/fs/snode.h> /* defines: VTOS */
34 #include <sys/fs/dv_node.h> /* declares: devfs_lookupname */
35 #include <sys/bootconf.h>
36 #include <sys/bootprops.h>
37 #include <netinet/in.h>
38 #include "iscsi.h"
39 #include <sys/ksocket.h>
40
41 /*
42 * This is a high level description of the default
43 * iscsi_net transport interfaces. These are used
44 * to create, send, recv, and close standard TCP/IP
45 * messages. In addition there are extensions to send
46 * and recv iSCSI PDU data.
47 *
48 * NOTE: It would be very easy for an iSCSI HBA vendor
49 * to register their own functions over the top of
50 * the default interfaces. This would allow an iSCSI
51 * HBA to use the same iscsiadm management interfaces
52 * and the Solaris iSCSI session / connection management.
53 * The current problem with this approach is we only
54 * allow one one registered transport table. This
55 * would be pretty easy to correct although will require
56 * additional CLI changes to manage multiple interfaces.
57 * If a vendor can present compelling performance data,
58 * then Sun will be willing to enhance this support for
59 * multiple interface tables and better CLI management.
60 *
61 * The following listing describes the iscsi_net
62 * entry points:
63 *
64 * socket - Creates TCP/IP socket connection. In the
65 * default implementation creates a sonode
66 * via the sockfs kernel layer.
67 * bind - Performs standard TCP/IP BSD operation. In
68 * the default implementation this only act
69 * as a soft binding based on the IP and routing
70 * tables. It would be preferred if this was
71 * a hard binding but that is currently not
72 * possible with Solaris's networking stack.
73 * connect - Performs standard TCP/IP BSD operation. This
74 * establishes the TCP SYN to the peer IP address.
75 * listen - Performs standard TCP/IP BSD operation. This
76 * listens for incoming peer connections.
77 * accept - Performs standard TCP/IP BSD operation. This
78 * accepts incoming peer connections.
79 * shutdown - This disconnects the TCP/IP connection while
80 * maintaining the resources.
81 * close - This disconnects the TCP/IP connection and
82 * releases the resources.
83 *
84 * getsockopt - Gets socket option for specified socket.
85 * setsockopt - Sets socket option for specified socket.
86 *
87 * The current socket options that are used by the initiator
88 * are listed below.
89 *
90 * TCP_CONN_NOTIFY_THRESHOLD
91 * TCP_CONN_ABORT_THRESHOLD
92 * TCP_ABORT_THRESHOLD
93 * TCP_NODELAY
94 * SO_RCVBUF
95 * SO_SNDBUF
96 *
97 * iscsi_net_poll - Poll socket interface for a specified amount
98 * of data. If data not received in timeout
99 * period fail request.
100 * iscsi_net_sendmsg - Send message on socket connection
101 * iscsi_net_recvmsg - Receive message on socket connection
102 *
103 * iscsi_net_sendpdu - Send iSCSI PDU on socket connection
104 * iscsi_net_recvhdr - Receive iSCSI header on socket connection
105 * iscsi_net_recvdata - Receive iSCSI data on socket connection
106 *
107 * The iSCSI interfaces have the below optional flags.
108 *
109 * ISCSI_NET_HEADER_DIGEST - The interface should either
110 * generate or validate the iSCSI
111 * header digest CRC.
112 * ISCSI_NET_DATA_DIGESt - The interface should either
113 * generate or validate the iSCSI
114 * data digest CRC.
115 */
116
117
118 /* global */
119 iscsi_network_t *iscsi_net;
120
121 /* consts */
122
123 /*
124 * This table is used for quick validation of incoming
125 * iSCSI PDU opcodes. A value of '0' in the table below
126 * indicated that the opcode is invalid for an iSCSI
127 * initiator to receive.
128 */
129 const int is_incoming_opcode_invalid[256] = {
130 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
131 /* 0x0X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
132 /* 0x1X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
133 /* 0x2X */ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
134 /* 0x3X */ 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
135 /* 0x4X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
136 /* 0x5X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
137 /* 0x6X */ 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
138 /* 0x7X */ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
139 /* 0x8X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
140 /* 0x9X */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
141 /* 0xAX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
142 /* 0xBX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
143 /* 0xCX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
144 /* 0xDX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
145 /* 0xEX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
146 /* 0xFX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
147 };
148
149 #define IP_4_BITS 32
150 #define IP_6_BITS 128
151
152 extern ib_boot_prop_t *iscsiboot_prop;
153
154 /* prototypes */
155 static void * iscsi_net_socket(int domain, int type, int protocol);
156 static int iscsi_net_bind(void *socket, struct sockaddr *
157 name, int name_len, int backlog, int flags);
158 static int iscsi_net_connect(void *socket, struct sockaddr *
159 name, int name_len, int fflag, int flags);
160 static int iscsi_net_listen(void *socket, int backlog);
161 static void * iscsi_net_accept(void *socket, struct sockaddr *addr,
162 int *addr_len);
163 static int iscsi_net_getsockname(void *socket, struct sockaddr *, socklen_t *);
164 static int iscsi_net_getsockopt(void *socket, int level,
165 int option_name, void *option_val, int *option_len, int flags);
166 static int iscsi_net_setsockopt(void *socket, int level,
167 int option_name, void *option_val, int option_len);
168 static int iscsi_net_shutdown(void *socket, int how);
169 static void iscsi_net_close(void *socket);
170
171 static size_t iscsi_net_poll(void *socket, clock_t timeout);
172 static size_t iscsi_net_sendmsg(void *socket, struct msghdr *msg);
173 static size_t iscsi_net_recvmsg(void *socket,
174 struct msghdr *msg, int timeout);
175
176 static iscsi_status_t iscsi_net_sendpdu(void *socket, iscsi_hdr_t *ihp,
177 char *data, int flags);
178 static iscsi_status_t iscsi_net_recvdata(void *socket, iscsi_hdr_t *ihp,
179 char *data, int max_data_length, int timeout, int flags);
180 static iscsi_status_t iscsi_net_recvhdr(void *socket, iscsi_hdr_t *ihp,
181 int header_length, int timeout, int flags);
182
183 static void iscsi_net_set_connect_options(void *socket);
184
185 /*
186 * +--------------------------------------------------------------------+
187 * | network interface registration functions |
188 * +--------------------------------------------------------------------+
189 */
190
191 /*
192 * iscsi_net_init - initialize network interface
193 */
194 void
iscsi_net_init()195 iscsi_net_init()
196 {
197 iscsi_net = kmem_zalloc(sizeof (*iscsi_net), KM_SLEEP);
198
199 iscsi_net->socket = iscsi_net_socket;
200
201 iscsi_net->bind = iscsi_net_bind;
202 iscsi_net->connect = iscsi_net_connect;
203 iscsi_net->listen = iscsi_net_listen;
204 iscsi_net->accept = iscsi_net_accept;
205 iscsi_net->shutdown = iscsi_net_shutdown;
206 iscsi_net->close = iscsi_net_close;
207
208 iscsi_net->getsockname = iscsi_net_getsockname;
209 iscsi_net->getsockopt = iscsi_net_getsockopt;
210 iscsi_net->setsockopt = iscsi_net_setsockopt;
211
212 iscsi_net->poll = iscsi_net_poll;
213 iscsi_net->sendmsg = iscsi_net_sendmsg;
214 iscsi_net->recvmsg = iscsi_net_recvmsg;
215
216 iscsi_net->sendpdu = iscsi_net_sendpdu;
217 iscsi_net->recvhdr = iscsi_net_recvhdr;
218 iscsi_net->recvdata = iscsi_net_recvdata;
219 }
220
221 /*
222 * iscsi_net_fini - release network interface
223 */
224 void
iscsi_net_fini()225 iscsi_net_fini()
226 {
227 kmem_free(iscsi_net, sizeof (*iscsi_net));
228 iscsi_net = NULL;
229 }
230
231 /*
232 * iscsi_net_set_connect_options -
233 */
234 static void
iscsi_net_set_connect_options(void * socket)235 iscsi_net_set_connect_options(void *socket)
236 {
237 int ret = 0;
238 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP,
239 TCP_CONN_NOTIFY_THRESHOLD, (char *)&iscsi_net->tweaks.
240 conn_notify_threshold, sizeof (int));
241 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP,
242 TCP_CONN_ABORT_THRESHOLD, (char *)&iscsi_net->tweaks.
243 conn_abort_threshold, sizeof (int));
244 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, TCP_ABORT_THRESHOLD,
245 (char *)&iscsi_net->tweaks.abort_threshold, sizeof (int));
246 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
247 (char *)&iscsi_net->tweaks.nodelay, sizeof (int));
248 ret += iscsi_net->setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
249 (char *)&iscsi_net->tweaks.rcvbuf, sizeof (int));
250 ret += iscsi_net->setsockopt(socket, SOL_SOCKET, SO_SNDBUF,
251 (char *)&iscsi_net->tweaks.sndbuf, sizeof (int));
252 if (ret != 0) {
253 cmn_err(CE_NOTE, "iscsi connection failed to set socket option"
254 "TCP_CONN_NOTIFY_THRESHOLD, TCP_CONN_ABORT_THRESHOLD,"
255 "TCP_ABORT_THRESHOLD, TCP_NODELAY, SO_RCVBUF or SO_SNDBUF");
256 }
257 }
258
259 /*
260 * +--------------------------------------------------------------------+
261 * | register network interfaces |
262 * +--------------------------------------------------------------------+
263 */
264
265 /*
266 * iscsi_net_socket - create socket
267 */
268 static void *
iscsi_net_socket(int domain,int type,int protocol)269 iscsi_net_socket(int domain, int type, int protocol)
270 {
271 ksocket_t socket;
272 int err = 0;
273
274 err = ksocket_socket(&socket, domain, type, protocol, KSOCKET_SLEEP,
275 CRED());
276 if (!err)
277 return ((void *)socket);
278 else
279 return (NULL);
280
281 }
282
283 /*
284 * iscsi_net_bind - bind socket to a specific sockaddr
285 */
286 /* ARGSUSED */
287 static int
iscsi_net_bind(void * socket,struct sockaddr * name,int name_len,int backlog,int flags)288 iscsi_net_bind(void *socket, struct sockaddr *name, int name_len,
289 int backlog, int flags)
290 {
291 ksocket_t ks = (ksocket_t)socket;
292 int error;
293 error = ksocket_bind(ks, name, name_len, CRED());
294 if (error == 0 && backlog != 0)
295 error = ksocket_listen(ks, backlog, CRED());
296
297 return (error);
298 }
299
300 /*
301 * iscsi_net_connect - connect socket to peer sockaddr
302 */
303 /* ARGSUSED */
304 static int
iscsi_net_connect(void * socket,struct sockaddr * name,int name_len,int fflag,int flags)305 iscsi_net_connect(void *socket, struct sockaddr *name, int name_len,
306 int fflag, int flags)
307 {
308 ksocket_t ks = (ksocket_t)socket;
309 int rval;
310
311 iscsi_net_set_connect_options(socket);
312 rval = ksocket_connect(ks, name, name_len, CRED());
313
314 return (rval);
315 }
316
317 /*
318 * iscsi_net_listen - listen to socket for peer connections
319 */
320 static int
iscsi_net_listen(void * socket,int backlog)321 iscsi_net_listen(void *socket, int backlog)
322 {
323 ksocket_t ks = (ksocket_t)socket;
324 return (ksocket_listen(ks, backlog, CRED()));
325 }
326
327 /*
328 * iscsi_net_accept - accept peer socket connections
329 */
330 static void *
iscsi_net_accept(void * socket,struct sockaddr * addr,int * addr_len)331 iscsi_net_accept(void *socket, struct sockaddr *addr, int *addr_len)
332 {
333 ksocket_t listen_ks;
334 ksocket_t ks = (ksocket_t)socket;
335
336 (void) ksocket_accept(ks, addr, (socklen_t *)addr_len, &listen_ks,
337 CRED());
338
339 return ((void *)listen_ks);
340 }
341
342 /*
343 * iscsi_net_getsockname -
344 */
345 static int
iscsi_net_getsockname(void * socket,struct sockaddr * addr,socklen_t * addrlen)346 iscsi_net_getsockname(void *socket, struct sockaddr *addr, socklen_t *addrlen)
347 {
348 ksocket_t ks = (ksocket_t)socket;
349 return (ksocket_getsockname(ks, addr, addrlen, CRED()));
350 }
351
352 /*
353 * iscsi_net_getsockopt - get value of option on socket
354 */
355 /* ARGSUSED */
356 static int
iscsi_net_getsockopt(void * socket,int level,int option_name,void * option_val,int * option_len,int flags)357 iscsi_net_getsockopt(void *socket, int level, int option_name,
358 void *option_val, int *option_len, int flags)
359 {
360 ksocket_t ks = (ksocket_t)socket;
361 return (ksocket_getsockopt(ks, level, option_name, option_val,
362 option_len, CRED()));
363 }
364
365 /*
366 * iscsi_net_setsockopt - set value for option on socket
367 */
368 static int
iscsi_net_setsockopt(void * socket,int level,int option_name,void * option_val,int option_len)369 iscsi_net_setsockopt(void *socket, int level, int option_name,
370 void *option_val, int option_len)
371 {
372 ksocket_t ks = (ksocket_t)socket;
373 return (ksocket_setsockopt(ks, level, option_name, option_val,
374 option_len, CRED()));
375 }
376
377 /*
378 * iscsi_net_shutdown - shutdown socket connection
379 */
380 static int
iscsi_net_shutdown(void * socket,int how)381 iscsi_net_shutdown(void *socket, int how)
382 {
383 ksocket_t ks = (ksocket_t)socket;
384 return (ksocket_shutdown(ks, how, CRED()));
385 }
386
387 /*
388 * iscsi_net_close - shutdown socket connection and release resources
389 */
390 static void
iscsi_net_close(void * socket)391 iscsi_net_close(void *socket)
392 {
393 ksocket_t ks = (ksocket_t)socket;
394 (void) ksocket_close(ks, CRED());
395 }
396
397 /*
398 * iscsi_net_poll - poll socket for data
399 */
400 /* ARGSUSED */
401 static size_t
iscsi_net_poll(void * socket,clock_t timeout)402 iscsi_net_poll(void *socket, clock_t timeout)
403 {
404 int pflag;
405 char msg[64];
406 size_t recv = 0;
407 ksocket_t ks = (ksocket_t)socket;
408
409 if (get_udatamodel() == DATAMODEL_NONE ||
410 get_udatamodel() == DATAMODEL_NATIVE) {
411 struct timeval tl;
412
413 /* timeout is millisecond */
414 tl.tv_sec = timeout / 1000;
415 tl.tv_usec = (timeout % 1000) * 1000;
416 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl,
417 sizeof (struct timeval), CRED()))
418 return (0);
419 } else {
420 struct timeval32 tl;
421
422 /* timeout is millisecond */
423 tl.tv_sec = timeout / 1000;
424 tl.tv_usec = (timeout % 1000) * 1000;
425 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl,
426 sizeof (struct timeval32), CRED()))
427 return (0);
428 }
429
430 pflag = MSG_ANY;
431 bzero(msg, sizeof (msg));
432 return (ksocket_recv(ks, msg, sizeof (msg), pflag, &recv, CRED()));
433 }
434
435 /*
436 * iscsi_net_sendmsg - send message on socket
437 */
438 /* ARGSUSED */
439 static size_t
iscsi_net_sendmsg(void * socket,struct msghdr * msg)440 iscsi_net_sendmsg(void *socket, struct msghdr *msg)
441 {
442 ksocket_t ks = (ksocket_t)socket;
443 size_t sent = 0;
444 int flag = msg->msg_flags;
445 (void) ksocket_sendmsg(ks, msg, flag, &sent, CRED());
446 DTRACE_PROBE1(ksocket_sendmsg, size_t, sent);
447 return (sent);
448 }
449
450 /*
451 * iscsi_net_recvmsg - receive message on socket
452 */
453 /* ARGSUSED */
454 static size_t
iscsi_net_recvmsg(void * socket,struct msghdr * msg,int timeout)455 iscsi_net_recvmsg(void *socket, struct msghdr *msg, int timeout)
456 {
457 int prflag = msg->msg_flags;
458 ksocket_t ks = (ksocket_t)socket;
459 size_t recv = 0;
460
461 /* Set recv timeout */
462 if (get_udatamodel() == DATAMODEL_NONE ||
463 get_udatamodel() == DATAMODEL_NATIVE) {
464 struct timeval tl;
465
466 tl.tv_sec = timeout;
467 tl.tv_usec = 0;
468 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl,
469 sizeof (struct timeval), CRED()))
470 return (0);
471 } else {
472 struct timeval32 tl;
473
474 tl.tv_sec = timeout;
475 tl.tv_usec = 0;
476 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl,
477 sizeof (struct timeval32), CRED()))
478 return (0);
479 }
480 /*
481 * Receive the requested data. Block until all
482 * data is received or timeout.
483 */
484 ksocket_hold(ks);
485 (void) ksocket_recvmsg(ks, msg, prflag, &recv, CRED());
486 ksocket_rele(ks);
487 DTRACE_PROBE1(ksocket_recvmsg, size_t, recv);
488 return (recv);
489 }
490
491 /*
492 * iscsi_net_sendpdu - send iscsi pdu on socket
493 */
494 static iscsi_status_t
iscsi_net_sendpdu(void * socket,iscsi_hdr_t * ihp,char * data,int flags)495 iscsi_net_sendpdu(void *socket, iscsi_hdr_t *ihp, char *data, int flags)
496 {
497 uint32_t pad;
498 uint32_t crc_hdr;
499 uint32_t crc_data;
500 uint32_t pad_len;
501 uint32_t data_len;
502 iovec_t iovec[ISCSI_MAX_IOVEC];
503 int iovlen = 0;
504 size_t total_len = 0;
505 size_t send_len;
506 struct msghdr msg;
507
508 ASSERT(socket != NULL);
509 ASSERT(ihp != NULL);
510
511 /*
512 * Let's send the header first. 'hlength' is in 32-bit
513 * quantities, so we need to multiply by four to get bytes
514 */
515 ASSERT(iovlen < ISCSI_MAX_IOVEC);
516 iovec[iovlen].iov_base = (void *)ihp;
517 iovec[iovlen].iov_len = sizeof (*ihp) + ihp->hlength * 4;
518 total_len += sizeof (*ihp) + ihp->hlength * 4;
519 iovlen++;
520
521 /* Let's transmit the header digest if we have to. */
522 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
523 ASSERT(iovlen < ISCSI_MAX_IOVEC);
524 /*
525 * Converting the calculated CRC via htonl is not
526 * necessary because iscsi_crc32c calculates
527 * the value as it expects to be written
528 */
529 crc_hdr = iscsi_crc32c((char *)ihp,
530 sizeof (iscsi_hdr_t) + ihp->hlength * 4);
531
532 iovec[iovlen].iov_base = (void *)&crc_hdr;
533 iovec[iovlen].iov_len = sizeof (crc_hdr);
534 total_len += sizeof (crc_hdr);
535 iovlen++;
536 }
537
538 /* Let's transmit the data if any. */
539 data_len = ntoh24(ihp->dlength);
540
541 if (data_len) {
542
543 ASSERT(iovlen < ISCSI_MAX_IOVEC);
544 iovec[iovlen].iov_base = (void *)data;
545 iovec[iovlen].iov_len = data_len;
546 total_len += data_len;
547 iovlen++;
548
549 pad_len = ((ISCSI_PAD_WORD_LEN -
550 (data_len & (ISCSI_PAD_WORD_LEN - 1))) &
551 (ISCSI_PAD_WORD_LEN - 1));
552
553 /* Let's transmit the data pad if any. */
554 if (pad_len) {
555
556 ASSERT(iovlen < ISCSI_MAX_IOVEC);
557 pad = 0;
558 iovec[iovlen].iov_base = (void *)&pad;
559 iovec[iovlen].iov_len = pad_len;
560 total_len += pad_len;
561 iovlen++;
562 }
563
564 /* Let's transmit the data digest if we have to. */
565 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
566
567 ASSERT(iovlen < ISCSI_MAX_IOVEC);
568 /*
569 * Converting the calculated CRC via htonl is not
570 * necessary because iscsi_crc32c calculates the
571 * value as it expects to be written
572 */
573 crc_data = iscsi_crc32c(data, data_len);
574 crc_data = iscsi_crc32c_continued(
575 (char *)&pad, pad_len, crc_data);
576
577 iovec[iovlen].iov_base = (void *)&crc_data;
578 iovec[iovlen].iov_len = sizeof (crc_data);
579 total_len += sizeof (crc_data);
580 iovlen++;
581 }
582 }
583
584 DTRACE_PROBE4(tx, void *, socket, iovec_t *, &iovec[0],
585 int, iovlen, int, total_len);
586
587 /* Initialization of the message header. */
588 bzero(&msg, sizeof (msg));
589 msg.msg_iov = &iovec[0];
590 msg.msg_flags = MSG_WAITALL;
591 msg.msg_iovlen = iovlen;
592
593 send_len = iscsi_net->sendmsg(socket, &msg);
594 DTRACE_PROBE2(sendmsg, size_t, total_len, size_t, send_len);
595 if (total_len != send_len) {
596 return (ISCSI_STATUS_TCP_TX_ERROR);
597 }
598 return (ISCSI_STATUS_SUCCESS);
599 }
600
601 /*
602 * iscsi_net_recvhdr - receive iscsi hdr on socket
603 */
604 static iscsi_status_t
iscsi_net_recvhdr(void * socket,iscsi_hdr_t * ihp,int header_length,int timeout,int flags)605 iscsi_net_recvhdr(void *socket, iscsi_hdr_t *ihp, int header_length,
606 int timeout, int flags)
607 {
608 iovec_t iov[ISCSI_MAX_IOVEC];
609 int iovlen = 1;
610 int total_len = 0;
611 uint32_t crc_actual = 0;
612 uint32_t crc_calculated = 0;
613 char *adhdr = NULL;
614 int adhdr_length = 0;
615 struct msghdr msg;
616 size_t recv_len;
617
618 ASSERT(socket != NULL);
619 ASSERT(ihp != NULL);
620
621 if (header_length < sizeof (iscsi_hdr_t)) {
622 ASSERT(FALSE);
623 return (ISCSI_STATUS_INTERNAL_ERROR);
624 }
625
626 /*
627 * Receive primary header
628 */
629 iov[0].iov_base = (char *)ihp;
630 iov[0].iov_len = sizeof (iscsi_hdr_t);
631
632 bzero(&msg, sizeof (msg));
633 msg.msg_iov = iov;
634 msg.msg_flags = MSG_WAITALL;
635 msg.msg_iovlen = iovlen;
636
637 recv_len = iscsi_net->recvmsg(socket, &msg, timeout);
638 if (recv_len != sizeof (iscsi_hdr_t)) {
639 return (ISCSI_STATUS_TCP_RX_ERROR);
640 }
641
642 DTRACE_PROBE2(rx_hdr, void *, socket, iovec_t *iop, &iov[0]);
643
644 /* verify incoming opcode is a valid operation */
645 if (is_incoming_opcode_invalid[ihp->opcode]) {
646 cmn_err(CE_WARN, "iscsi connection(%p) protocol error - "
647 "received an unsupported opcode:0x%02x",
648 socket, ihp->opcode);
649 return (ISCSI_STATUS_PROTOCOL_ERROR);
650 }
651
652 /*
653 * Setup receipt of additional header
654 */
655 if (ihp->hlength > 0) {
656 adhdr = ((char *)ihp) + sizeof (iscsi_hdr_t);
657 adhdr_length = header_length - sizeof (iscsi_hdr_t);
658 /* make sure enough space is available for adhdr */
659 if (ihp->hlength > adhdr_length) {
660 ASSERT(FALSE);
661 return (ISCSI_STATUS_INTERNAL_ERROR);
662 }
663
664 ASSERT(iovlen < ISCSI_MAX_IOVEC);
665 iov[iovlen].iov_base = adhdr;
666 iov[iovlen].iov_len = adhdr_length;
667 total_len += adhdr_length;
668 iovlen++;
669 }
670
671 /*
672 * Setup receipt of header digest if enabled and connection
673 * is in full feature mode.
674 */
675 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
676 ASSERT(iovlen < ISCSI_MAX_IOVEC);
677 iov[iovlen].iov_base = (char *)&crc_actual;
678 iov[iovlen].iov_len = sizeof (uint32_t);
679 total_len += sizeof (uint32_t);
680 iovlen++;
681 }
682
683 /*
684 * Read additional header and/or header digest if pieces
685 * are available
686 */
687 if (iovlen > 1) {
688
689 bzero(&msg, sizeof (msg));
690 msg.msg_iov = iov;
691 msg.msg_flags = MSG_WAITALL;
692 msg.msg_iovlen = iovlen;
693
694 recv_len = iscsi_net->recvmsg(socket, &msg, timeout);
695 if (recv_len != total_len) {
696 return (ISCSI_STATUS_TCP_RX_ERROR);
697 }
698
699 DTRACE_PROBE4(rx_adhdr_digest, void *, socket,
700 iovec_t *iop, &iov[0], int, iovlen, int, total_len);
701
702 /*
703 * Verify header digest if enabled and connection
704 * is in full feature mode
705 */
706 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) {
707 crc_calculated = iscsi_crc32c((uchar_t *)ihp,
708 sizeof (iscsi_hdr_t) + ihp->hlength * 4);
709
710 /*
711 * Converting actual CRC read via ntohl is not
712 * necessary because iscsi_crc32c calculates the
713 * value as it expect to be read
714 */
715 if (crc_calculated != crc_actual) {
716 /* Invalid Header Digest */
717 cmn_err(CE_WARN, "iscsi connection(%p) "
718 "protocol error - encountered a header "
719 "digest error expected:0x%08x "
720 "received:0x%08x", socket,
721 crc_calculated, crc_actual);
722 return (ISCSI_STATUS_HEADER_DIGEST_ERROR);
723 }
724 }
725 }
726 return (ISCSI_STATUS_SUCCESS);
727 }
728
729
730 /*
731 * iscsi_net_recvdata - receive iscsi data payload from socket
732 */
733 static iscsi_status_t
iscsi_net_recvdata(void * socket,iscsi_hdr_t * ihp,char * data,int max_data_length,int timeout,int flags)734 iscsi_net_recvdata(void *socket, iscsi_hdr_t *ihp, char *data,
735 int max_data_length, int timeout, int flags)
736 {
737 struct iovec iov[3];
738 int iovlen = 1;
739 int total_len = 0;
740 int dlength = 0;
741 int pad_len = 0;
742 uint8_t pad[ISCSI_PAD_WORD_LEN];
743 uint32_t crc_calculated = 0;
744 uint32_t crc_actual = 0;
745 struct msghdr msg;
746 size_t recv_len;
747
748 ASSERT(socket != NULL);
749 ASSERT(ihp != NULL);
750 ASSERT(data != NULL);
751
752 /* short hand dlength */
753 dlength = ntoh24(ihp->dlength);
754
755 /* verify dlength is valid */
756 if (dlength > max_data_length) {
757 cmn_err(CE_WARN, "iscsi connection(%p) protocol error - "
758 "invalid data lengths itt:0x%x received:0x%x "
759 "max expected:0x%x", socket, ihp->itt,
760 dlength, max_data_length);
761 return (ISCSI_STATUS_PROTOCOL_ERROR);
762 }
763
764 if (dlength) {
765 /* calculate pad */
766 pad_len = ((ISCSI_PAD_WORD_LEN -
767 (dlength & (ISCSI_PAD_WORD_LEN - 1))) &
768 (ISCSI_PAD_WORD_LEN - 1));
769
770 /* setup data iovec */
771 iov[0].iov_base = (char *)data;
772 iov[0].iov_len = dlength;
773 total_len = dlength;
774
775 /* if pad setup pad iovec */
776 if (pad_len) {
777 iov[iovlen].iov_base = (char *)&pad;
778 iov[iovlen].iov_len = pad_len;
779 total_len += pad_len;
780 iovlen++;
781 }
782
783 /* setup data digest */
784 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
785 iov[iovlen].iov_base = (char *)&crc_actual;
786 iov[iovlen].iov_len = sizeof (crc_actual);
787 total_len += sizeof (crc_actual);
788 iovlen++;
789 }
790
791 bzero(&msg, sizeof (msg));
792 msg.msg_iov = iov;
793 msg.msg_flags = MSG_WAITALL;
794 msg.msg_iovlen = iovlen;
795
796 recv_len = iscsi_net->recvmsg(socket, &msg, timeout);
797 if (recv_len != total_len) {
798 return (ISCSI_STATUS_TCP_RX_ERROR);
799 }
800
801 DTRACE_PROBE4(rx_data, void *, socket, iovec_t *iop,
802 &iov[0], int, iovlen, int, total_len);
803
804 /* verify data digest is present */
805 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) {
806
807 crc_calculated = iscsi_crc32c(data, dlength);
808 crc_calculated = iscsi_crc32c_continued(
809 (char *)&pad, pad_len, crc_calculated);
810
811 /*
812 * Converting actual CRC read via ntohl is not
813 * necessary because iscsi_crc32c calculates the
814 * value as it expects to be read
815 */
816 if (crc_calculated != crc_actual) {
817 cmn_err(CE_WARN, "iscsi connection(%p) "
818 "protocol error - encountered a data "
819 "digest error itt:0x%x expected:0x%08x "
820 "received:0x%08x", socket,
821 ihp->itt, crc_calculated, crc_actual);
822 return (ISCSI_STATUS_DATA_DIGEST_ERROR);
823 }
824 }
825 }
826 return (ISCSI_STATUS_SUCCESS);
827 }
828
829 /*
830 * Convert a prefix length to a mask.
831 */
832 static iscsi_status_t
iscsi_prefixlentomask(int prefixlen,int maxlen,uchar_t * mask)833 iscsi_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
834 {
835 if (prefixlen < 0 || prefixlen > maxlen || mask == NULL) {
836 return (ISCSI_STATUS_INTERNAL_ERROR);
837 }
838
839 while (prefixlen > 0) {
840 if (prefixlen >= 8) {
841 *mask = 0xff;
842 mask++;
843 prefixlen = prefixlen - 8;
844 continue;
845 }
846 *mask = *mask | (1 << (8 - prefixlen));
847 prefixlen--;
848 }
849 return (ISCSI_STATUS_SUCCESS);
850 }
851
852 iscsi_status_t
iscsi_net_interface(boolean_t reset)853 iscsi_net_interface(boolean_t reset)
854 {
855 struct in_addr braddr;
856 struct in_addr subnet;
857 struct in_addr myaddr;
858 struct in_addr defgateway;
859 struct in6_addr myaddr6;
860 struct in6_addr subnet6;
861 uchar_t mask_prefix = 0;
862 int mask_bits = 1;
863 TIUSER *tiptr;
864 TIUSER *tiptr6;
865 char ifname[16] = {0};
866 iscsi_status_t status;
867
868 struct knetconfig dl_udp_netconf = {
869 NC_TPI_CLTS,
870 NC_INET,
871 NC_UDP,
872 0, };
873 struct knetconfig dl_udp6_netconf = {
874 NC_TPI_CLTS,
875 NC_INET6,
876 NC_UDP,
877 0, };
878
879 (void) strlcpy(ifname, rootfs.bo_ifname, sizeof (ifname));
880
881 if (iscsiboot_prop->boot_nic.sin_family == AF_INET) {
882 /*
883 * Assumes only one linkage array element.
884 */
885 dl_udp_netconf.knc_rdev =
886 makedevice(clone_major, ddi_name_to_major("udp"));
887
888 myaddr.s_addr =
889 iscsiboot_prop->boot_nic.nic_ip_u.u_in4.s_addr;
890
891 mask_prefix = iscsiboot_prop->boot_nic.sub_mask_prefix;
892 (void) memset(&subnet.s_addr, 0, sizeof (subnet));
893 status = iscsi_prefixlentomask(mask_prefix, IP_4_BITS,
894 (uchar_t *)&subnet.s_addr);
895 if (status != ISCSI_STATUS_SUCCESS) {
896 return (status);
897 }
898
899 mask_bits = mask_bits << (IP_4_BITS - mask_prefix);
900 mask_bits = mask_bits - 1;
901 /*
902 * Set the last mask bits of the ip address with 1, then
903 * we can get the broadcast address.
904 */
905 braddr.s_addr = myaddr.s_addr | mask_bits;
906
907 defgateway.s_addr =
908 iscsiboot_prop->boot_nic.nic_gw_u.u_in4.s_addr;
909
910 /* initialize interface */
911 if (t_kopen((file_t *)NULL, dl_udp_netconf.knc_rdev,
912 FREAD|FWRITE, &tiptr, CRED()) == 0) {
913 int ret = 0;
914 if (reset == B_TRUE) {
915 ret = kdlifconfig(tiptr, AF_INET, &myaddr,
916 &subnet, NULL, NULL, ifname);
917 } else if (defgateway.s_addr == 0) {
918 /* No default gate way specified */
919 ret = kdlifconfig(tiptr, AF_INET, &myaddr,
920 &subnet, &braddr, NULL, ifname);
921 } else {
922 ret = kdlifconfig(tiptr, AF_INET, &myaddr,
923 &subnet, &braddr, &defgateway, ifname);
924 }
925 if (ret != 0) {
926 (void) t_kclose(tiptr, 0);
927 cmn_err(CE_WARN, "Failed to configure"
928 " iSCSI boot nic");
929 return (ISCSI_STATUS_INTERNAL_ERROR);
930 }
931 (void) t_kclose(tiptr, 0);
932 } else {
933 cmn_err(CE_WARN, "Failed to configure"
934 " iSCSI boot nic");
935 return (ISCSI_STATUS_INTERNAL_ERROR);
936 }
937 return (ISCSI_STATUS_SUCCESS);
938 } else {
939 dl_udp6_netconf.knc_rdev =
940 makedevice(clone_major, ddi_name_to_major("udp6"));
941
942 bcopy(&iscsiboot_prop->boot_nic.nic_ip_u.u_in6.s6_addr,
943 &myaddr6.s6_addr, 16);
944
945 (void) memset(&subnet6, 0, sizeof (subnet6));
946 mask_prefix = iscsiboot_prop->boot_nic.sub_mask_prefix;
947 status = iscsi_prefixlentomask(mask_prefix, IP_6_BITS,
948 (uchar_t *)&subnet6.s6_addr);
949 if (status != ISCSI_STATUS_SUCCESS) {
950 return (status);
951 }
952
953 if (t_kopen((file_t *)NULL, dl_udp6_netconf.knc_rdev,
954 FREAD|FWRITE, &tiptr6, CRED()) == 0) {
955 if (kdlifconfig(tiptr6, AF_INET6, &myaddr6,
956 &subnet6, NULL, NULL, ifname)) {
957 cmn_err(CE_WARN, "Failed to configure"
958 " iSCSI boot nic");
959 (void) t_kclose(tiptr6, 0);
960 return (ISCSI_STATUS_INTERNAL_ERROR);
961 }
962 (void) t_kclose(tiptr6, 0);
963 } else {
964 cmn_err(CE_WARN, "Failed to configure"
965 " iSCSI boot nic");
966 return (ISCSI_STATUS_INTERNAL_ERROR);
967 }
968 return (ISCSI_STATUS_SUCCESS);
969 }
970 }
971