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 2009 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 int modrootloaded; 153 extern ib_boot_prop_t *iscsiboot_prop; 154 155 /* prototypes */ 156 157 /* for iSCSI boot */ 158 static int net_up = 0; 159 static iscsi_status_t iscsi_net_interface(); 160 /* boot prototypes end */ 161 162 static void * iscsi_net_socket(int domain, int type, int protocol); 163 static int iscsi_net_bind(void *socket, struct sockaddr * 164 name, int name_len, int backlog, int flags); 165 static int iscsi_net_connect(void *socket, struct sockaddr * 166 name, int name_len, int fflag, int flags); 167 static int iscsi_net_listen(void *socket, int backlog); 168 static void * iscsi_net_accept(void *socket, struct sockaddr *addr, 169 int *addr_len); 170 static int iscsi_net_getsockname(void *socket, struct sockaddr *, socklen_t *); 171 static int iscsi_net_getsockopt(void *socket, int level, 172 int option_name, void *option_val, int *option_len, int flags); 173 static int iscsi_net_setsockopt(void *socket, int level, 174 int option_name, void *option_val, int option_len); 175 static int iscsi_net_shutdown(void *socket, int how); 176 static void iscsi_net_close(void *socket); 177 178 static size_t iscsi_net_poll(void *socket, clock_t timeout); 179 static size_t iscsi_net_sendmsg(void *socket, struct msghdr *msg); 180 static size_t iscsi_net_recvmsg(void *socket, 181 struct msghdr *msg, int timeout); 182 183 static iscsi_status_t iscsi_net_sendpdu(void *socket, iscsi_hdr_t *ihp, 184 char *data, int flags); 185 static iscsi_status_t iscsi_net_recvdata(void *socket, iscsi_hdr_t *ihp, 186 char *data, int max_data_length, int timeout, int flags); 187 static iscsi_status_t iscsi_net_recvhdr(void *socket, iscsi_hdr_t *ihp, 188 int header_length, int timeout, int flags); 189 190 static void iscsi_net_set_connect_options(void *socket); 191 192 /* 193 * +--------------------------------------------------------------------+ 194 * | network interface registration functions | 195 * +--------------------------------------------------------------------+ 196 */ 197 198 /* 199 * iscsi_net_init - initialize network interface 200 */ 201 void 202 iscsi_net_init() 203 { 204 iscsi_net = kmem_zalloc(sizeof (*iscsi_net), KM_SLEEP); 205 206 iscsi_net->socket = iscsi_net_socket; 207 208 iscsi_net->bind = iscsi_net_bind; 209 iscsi_net->connect = iscsi_net_connect; 210 iscsi_net->listen = iscsi_net_listen; 211 iscsi_net->accept = iscsi_net_accept; 212 iscsi_net->shutdown = iscsi_net_shutdown; 213 iscsi_net->close = iscsi_net_close; 214 215 iscsi_net->getsockname = iscsi_net_getsockname; 216 iscsi_net->getsockopt = iscsi_net_getsockopt; 217 iscsi_net->setsockopt = iscsi_net_setsockopt; 218 219 iscsi_net->poll = iscsi_net_poll; 220 iscsi_net->sendmsg = iscsi_net_sendmsg; 221 iscsi_net->recvmsg = iscsi_net_recvmsg; 222 223 iscsi_net->sendpdu = iscsi_net_sendpdu; 224 iscsi_net->recvhdr = iscsi_net_recvhdr; 225 iscsi_net->recvdata = iscsi_net_recvdata; 226 } 227 228 /* 229 * iscsi_net_fini - release network interface 230 */ 231 void 232 iscsi_net_fini() 233 { 234 kmem_free(iscsi_net, sizeof (*iscsi_net)); 235 iscsi_net = NULL; 236 } 237 238 /* 239 * iscsi_net_set_connect_options - 240 */ 241 static void 242 iscsi_net_set_connect_options(void *socket) 243 { 244 int ret = 0; 245 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, 246 TCP_CONN_NOTIFY_THRESHOLD, (char *)&iscsi_net->tweaks. 247 conn_notify_threshold, sizeof (int)); 248 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, 249 TCP_CONN_ABORT_THRESHOLD, (char *)&iscsi_net->tweaks. 250 conn_abort_threshold, sizeof (int)); 251 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, TCP_ABORT_THRESHOLD, 252 (char *)&iscsi_net->tweaks.abort_threshold, sizeof (int)); 253 ret += iscsi_net->setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, 254 (char *)&iscsi_net->tweaks.nodelay, sizeof (int)); 255 ret += iscsi_net->setsockopt(socket, SOL_SOCKET, SO_RCVBUF, 256 (char *)&iscsi_net->tweaks.rcvbuf, sizeof (int)); 257 ret += iscsi_net->setsockopt(socket, SOL_SOCKET, SO_SNDBUF, 258 (char *)&iscsi_net->tweaks.sndbuf, sizeof (int)); 259 if (ret != 0) { 260 cmn_err(CE_NOTE, "iscsi connection failed to set socket option" 261 "TCP_CONN_NOTIFY_THRESHOLD, TCP_CONN_ABORT_THRESHOLD," 262 "TCP_ABORT_THRESHOLD, TCP_NODELAY, SO_RCVBUF or SO_SNDBUF"); 263 } 264 } 265 266 /* 267 * +--------------------------------------------------------------------+ 268 * | register network interfaces | 269 * +--------------------------------------------------------------------+ 270 */ 271 272 /* 273 * iscsi_net_socket - create socket 274 */ 275 static void * 276 iscsi_net_socket(int domain, int type, int protocol) 277 { 278 ksocket_t socket; 279 int err = 0; 280 281 if (!modrootloaded && !net_up && iscsiboot_prop) { 282 if (iscsi_net_interface() == ISCSI_STATUS_SUCCESS) 283 net_up = 1; 284 } 285 286 err = ksocket_socket(&socket, domain, type, protocol, KSOCKET_SLEEP, 287 CRED()); 288 if (!err) 289 return ((void *)socket); 290 else 291 return (NULL); 292 293 } 294 295 /* 296 * iscsi_net_bind - bind socket to a specific sockaddr 297 */ 298 /* ARGSUSED */ 299 static int 300 iscsi_net_bind(void *socket, struct sockaddr *name, int name_len, 301 int backlog, int flags) 302 { 303 ksocket_t ks = (ksocket_t)socket; 304 int error; 305 error = ksocket_bind(ks, name, name_len, CRED()); 306 if (error == 0 && backlog != 0) 307 error = ksocket_listen(ks, backlog, CRED()); 308 309 return (error); 310 } 311 312 /* 313 * iscsi_net_connect - connect socket to peer sockaddr 314 */ 315 /* ARGSUSED */ 316 static int 317 iscsi_net_connect(void *socket, struct sockaddr *name, int name_len, 318 int fflag, int flags) 319 { 320 ksocket_t ks = (ksocket_t)socket; 321 int rval; 322 323 iscsi_net_set_connect_options(socket); 324 rval = ksocket_connect(ks, name, name_len, CRED()); 325 326 return (rval); 327 } 328 329 /* 330 * iscsi_net_listen - listen to socket for peer connections 331 */ 332 static int 333 iscsi_net_listen(void *socket, int backlog) 334 { 335 ksocket_t ks = (ksocket_t)socket; 336 return (ksocket_listen(ks, backlog, CRED())); 337 } 338 339 /* 340 * iscsi_net_accept - accept peer socket connections 341 */ 342 static void * 343 iscsi_net_accept(void *socket, struct sockaddr *addr, int *addr_len) 344 { 345 ksocket_t listen_ks; 346 ksocket_t ks = (ksocket_t)socket; 347 348 ksocket_accept(ks, addr, (socklen_t *)addr_len, &listen_ks, CRED()); 349 350 return ((void *)listen_ks); 351 } 352 353 /* 354 * iscsi_net_getsockname - 355 */ 356 static int 357 iscsi_net_getsockname(void *socket, struct sockaddr *addr, socklen_t *addrlen) 358 { 359 ksocket_t ks = (ksocket_t)socket; 360 return (ksocket_getsockname(ks, addr, addrlen, CRED())); 361 } 362 363 /* 364 * iscsi_net_getsockopt - get value of option on socket 365 */ 366 /* ARGSUSED */ 367 static int 368 iscsi_net_getsockopt(void *socket, int level, int option_name, 369 void *option_val, int *option_len, int flags) 370 { 371 ksocket_t ks = (ksocket_t)socket; 372 return (ksocket_getsockopt(ks, level, option_name, option_val, 373 option_len, CRED())); 374 } 375 376 /* 377 * iscsi_net_setsockopt - set value for option on socket 378 */ 379 static int 380 iscsi_net_setsockopt(void *socket, int level, int option_name, 381 void *option_val, int option_len) 382 { 383 ksocket_t ks = (ksocket_t)socket; 384 return (ksocket_setsockopt(ks, level, option_name, option_val, 385 option_len, CRED())); 386 } 387 388 /* 389 * iscsi_net_shutdown - shutdown socket connection 390 */ 391 static int 392 iscsi_net_shutdown(void *socket, int how) 393 { 394 ksocket_t ks = (ksocket_t)socket; 395 return (ksocket_shutdown(ks, how, CRED())); 396 } 397 398 /* 399 * iscsi_net_close - shutdown socket connection and release resources 400 */ 401 static void 402 iscsi_net_close(void *socket) 403 { 404 ksocket_t ks = (ksocket_t)socket; 405 (void) ksocket_close(ks, CRED()); 406 } 407 408 /* 409 * iscsi_net_poll - poll socket for data 410 */ 411 /* ARGSUSED */ 412 static size_t 413 iscsi_net_poll(void *socket, clock_t timeout) 414 { 415 int pflag; 416 char msg[64]; 417 size_t recv = 0; 418 ksocket_t ks = (ksocket_t)socket; 419 420 if (get_udatamodel() == DATAMODEL_NONE || 421 get_udatamodel() == DATAMODEL_NATIVE) { 422 struct timeval tl; 423 424 /* timeout is millisecond */ 425 tl.tv_sec = timeout / 1000; 426 tl.tv_usec = (timeout % 1000) * 1000; 427 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl, 428 sizeof (struct timeval), CRED())) 429 return (0); 430 } else { 431 struct timeval32 tl; 432 433 /* timeout is millisecond */ 434 tl.tv_sec = timeout / 1000; 435 tl.tv_usec = (timeout % 1000) * 1000; 436 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl, 437 sizeof (struct timeval32), CRED())) 438 return (0); 439 } 440 441 pflag = MSG_ANY; 442 bzero(msg, sizeof (msg)); 443 return (ksocket_recv(ks, msg, sizeof (msg), pflag, &recv, CRED())); 444 } 445 446 /* 447 * iscsi_net_sendmsg - send message on socket 448 */ 449 /* ARGSUSED */ 450 static size_t 451 iscsi_net_sendmsg(void *socket, struct msghdr *msg) 452 { 453 ksocket_t ks = (ksocket_t)socket; 454 size_t sent = 0; 455 int flag = msg->msg_flags; 456 (void) ksocket_sendmsg(ks, msg, flag, &sent, CRED()); 457 DTRACE_PROBE1(ksocket_sendmsg, size_t, sent); 458 return (sent); 459 } 460 461 /* 462 * iscsi_net_recvmsg - receive message on socket 463 */ 464 /* ARGSUSED */ 465 static size_t 466 iscsi_net_recvmsg(void *socket, struct msghdr *msg, int timeout) 467 { 468 int prflag = msg->msg_flags; 469 ksocket_t ks = (ksocket_t)socket; 470 size_t recv = 0; 471 472 /* Set recv timeout */ 473 if (get_udatamodel() == DATAMODEL_NONE || 474 get_udatamodel() == DATAMODEL_NATIVE) { 475 struct timeval tl; 476 477 tl.tv_sec = timeout; 478 tl.tv_usec = 0; 479 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl, 480 sizeof (struct timeval), CRED())) 481 return (0); 482 } else { 483 struct timeval32 tl; 484 485 tl.tv_sec = timeout; 486 tl.tv_usec = 0; 487 if (ksocket_setsockopt(ks, SOL_SOCKET, SO_RCVTIMEO, &tl, 488 sizeof (struct timeval32), CRED())) 489 return (0); 490 } 491 /* 492 * Receive the requested data. Block until all 493 * data is received or timeout. 494 */ 495 ksocket_hold(ks); 496 ksocket_recvmsg(ks, msg, prflag, &recv, CRED()); 497 ksocket_rele(ks); 498 DTRACE_PROBE1(ksocket_recvmsg, size_t, recv); 499 return (recv); 500 } 501 502 /* 503 * iscsi_net_sendpdu - send iscsi pdu on socket 504 */ 505 static iscsi_status_t 506 iscsi_net_sendpdu(void *socket, iscsi_hdr_t *ihp, char *data, int flags) 507 { 508 uint32_t pad; 509 uint32_t crc_hdr; 510 uint32_t crc_data; 511 uint32_t pad_len; 512 uint32_t data_len; 513 iovec_t iovec[ISCSI_MAX_IOVEC]; 514 int iovlen = 0; 515 size_t total_len = 0; 516 size_t send_len; 517 struct msghdr msg; 518 519 ASSERT(socket != NULL); 520 ASSERT(ihp != NULL); 521 522 /* 523 * Let's send the header first. 'hlength' is in 32-bit 524 * quantities, so we need to multiply by four to get bytes 525 */ 526 ASSERT(iovlen < ISCSI_MAX_IOVEC); 527 iovec[iovlen].iov_base = (void *)ihp; 528 iovec[iovlen].iov_len = sizeof (*ihp) + ihp->hlength * 4; 529 total_len += sizeof (*ihp) + ihp->hlength * 4; 530 iovlen++; 531 532 /* Let's transmit the header digest if we have to. */ 533 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) { 534 ASSERT(iovlen < ISCSI_MAX_IOVEC); 535 /* 536 * Converting the calculated CRC via htonl is not 537 * necessary because iscsi_crc32c calculates 538 * the value as it expects to be written 539 */ 540 crc_hdr = iscsi_crc32c((char *)ihp, 541 sizeof (iscsi_hdr_t) + ihp->hlength * 4); 542 543 iovec[iovlen].iov_base = (void *)&crc_hdr; 544 iovec[iovlen].iov_len = sizeof (crc_hdr); 545 total_len += sizeof (crc_hdr); 546 iovlen++; 547 } 548 549 /* Let's transmit the data if any. */ 550 data_len = ntoh24(ihp->dlength); 551 552 if (data_len) { 553 554 ASSERT(iovlen < ISCSI_MAX_IOVEC); 555 iovec[iovlen].iov_base = (void *)data; 556 iovec[iovlen].iov_len = data_len; 557 total_len += data_len; 558 iovlen++; 559 560 pad_len = ((ISCSI_PAD_WORD_LEN - 561 (data_len & (ISCSI_PAD_WORD_LEN - 1))) & 562 (ISCSI_PAD_WORD_LEN - 1)); 563 564 /* Let's transmit the data pad if any. */ 565 if (pad_len) { 566 567 ASSERT(iovlen < ISCSI_MAX_IOVEC); 568 pad = 0; 569 iovec[iovlen].iov_base = (void *)&pad; 570 iovec[iovlen].iov_len = pad_len; 571 total_len += pad_len; 572 iovlen++; 573 } 574 575 /* Let's transmit the data digest if we have to. */ 576 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) { 577 578 ASSERT(iovlen < ISCSI_MAX_IOVEC); 579 /* 580 * Converting the calculated CRC via htonl is not 581 * necessary because iscsi_crc32c calculates the 582 * value as it expects to be written 583 */ 584 crc_data = iscsi_crc32c(data, data_len); 585 crc_data = iscsi_crc32c_continued( 586 (char *)&pad, pad_len, crc_data); 587 588 iovec[iovlen].iov_base = (void *)&crc_data; 589 iovec[iovlen].iov_len = sizeof (crc_data); 590 total_len += sizeof (crc_data); 591 iovlen++; 592 } 593 } 594 595 DTRACE_PROBE4(tx, void *, socket, iovec_t *, &iovec[0], 596 int, iovlen, int, total_len); 597 598 /* Initialization of the message header. */ 599 bzero(&msg, sizeof (msg)); 600 msg.msg_iov = &iovec[0]; 601 msg.msg_flags = MSG_WAITALL; 602 msg.msg_iovlen = iovlen; 603 604 send_len = iscsi_net->sendmsg(socket, &msg); 605 DTRACE_PROBE2(sendmsg, size_t, total_len, size_t, send_len); 606 if (total_len != send_len) { 607 return (ISCSI_STATUS_TCP_TX_ERROR); 608 } 609 return (ISCSI_STATUS_SUCCESS); 610 } 611 612 /* 613 * iscsi_net_recvhdr - receive iscsi hdr on socket 614 */ 615 static iscsi_status_t 616 iscsi_net_recvhdr(void *socket, iscsi_hdr_t *ihp, int header_length, 617 int timeout, int flags) 618 { 619 iovec_t iov[ISCSI_MAX_IOVEC]; 620 int iovlen = 1; 621 int total_len = 0; 622 uint32_t crc_actual = 0; 623 uint32_t crc_calculated = 0; 624 char *adhdr = NULL; 625 int adhdr_length = 0; 626 struct msghdr msg; 627 size_t recv_len; 628 629 ASSERT(socket != NULL); 630 ASSERT(ihp != NULL); 631 632 if (header_length < sizeof (iscsi_hdr_t)) { 633 ASSERT(FALSE); 634 return (ISCSI_STATUS_INTERNAL_ERROR); 635 } 636 637 /* 638 * Receive primary header 639 */ 640 iov[0].iov_base = (char *)ihp; 641 iov[0].iov_len = sizeof (iscsi_hdr_t); 642 643 bzero(&msg, sizeof (msg)); 644 msg.msg_iov = iov; 645 msg.msg_flags = MSG_WAITALL; 646 msg.msg_iovlen = iovlen; 647 648 recv_len = iscsi_net->recvmsg(socket, &msg, timeout); 649 if (recv_len != sizeof (iscsi_hdr_t)) { 650 return (ISCSI_STATUS_TCP_RX_ERROR); 651 } 652 653 DTRACE_PROBE2(rx_hdr, void *, socket, iovec_t *iop, &iov[0]); 654 655 /* verify incoming opcode is a valid operation */ 656 if (is_incoming_opcode_invalid[ihp->opcode]) { 657 cmn_err(CE_WARN, "iscsi connection(%p) protocol error - " 658 "received an unsupported opcode:0x%02x", 659 socket, ihp->opcode); 660 return (ISCSI_STATUS_PROTOCOL_ERROR); 661 } 662 663 /* 664 * Setup receipt of additional header 665 */ 666 if (ihp->hlength > 0) { 667 adhdr = ((char *)ihp) + sizeof (iscsi_hdr_t); 668 adhdr_length = header_length - sizeof (iscsi_hdr_t); 669 /* make sure enough space is available for adhdr */ 670 if (ihp->hlength > adhdr_length) { 671 ASSERT(FALSE); 672 return (ISCSI_STATUS_INTERNAL_ERROR); 673 } 674 675 ASSERT(iovlen < ISCSI_MAX_IOVEC); 676 iov[iovlen].iov_base = adhdr; 677 iov[iovlen].iov_len = adhdr_length; 678 total_len += adhdr_length; 679 iovlen++; 680 } 681 682 /* 683 * Setup receipt of header digest if enabled and connection 684 * is in full feature mode. 685 */ 686 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) { 687 ASSERT(iovlen < ISCSI_MAX_IOVEC); 688 iov[iovlen].iov_base = (char *)&crc_actual; 689 iov[iovlen].iov_len = sizeof (uint32_t); 690 total_len += sizeof (uint32_t); 691 iovlen++; 692 } 693 694 /* 695 * Read additional header and/or header digest if pieces 696 * are available 697 */ 698 if (iovlen > 1) { 699 700 bzero(&msg, sizeof (msg)); 701 msg.msg_iov = iov; 702 msg.msg_flags = MSG_WAITALL; 703 msg.msg_iovlen = iovlen; 704 705 recv_len = iscsi_net->recvmsg(socket, &msg, timeout); 706 if (recv_len != total_len) { 707 return (ISCSI_STATUS_TCP_RX_ERROR); 708 } 709 710 DTRACE_PROBE4(rx_adhdr_digest, void *, socket, 711 iovec_t *iop, &iov[0], int, iovlen, int, total_len); 712 713 /* 714 * Verify header digest if enabled and connection 715 * is in full feature mode 716 */ 717 if ((flags & ISCSI_NET_HEADER_DIGEST) != 0) { 718 crc_calculated = iscsi_crc32c((uchar_t *)ihp, 719 sizeof (iscsi_hdr_t) + ihp->hlength * 4); 720 721 /* 722 * Converting actual CRC read via ntohl is not 723 * necessary because iscsi_crc32c calculates the 724 * value as it expect to be read 725 */ 726 if (crc_calculated != crc_actual) { 727 /* Invalid Header Digest */ 728 cmn_err(CE_WARN, "iscsi connection(%p) " 729 "protocol error - encountered a header " 730 "digest error expected:0x%08x " 731 "received:0x%08x", socket, 732 crc_calculated, crc_actual); 733 return (ISCSI_STATUS_HEADER_DIGEST_ERROR); 734 } 735 } 736 } 737 return (ISCSI_STATUS_SUCCESS); 738 } 739 740 741 /* 742 * iscsi_net_recvdata - receive iscsi data payload from socket 743 */ 744 static iscsi_status_t 745 iscsi_net_recvdata(void *socket, iscsi_hdr_t *ihp, char *data, 746 int max_data_length, int timeout, int flags) 747 { 748 struct iovec iov[3]; 749 int iovlen = 1; 750 int total_len = 0; 751 int dlength = 0; 752 int pad_len = 0; 753 uint8_t pad[ISCSI_PAD_WORD_LEN]; 754 uint32_t crc_calculated = 0; 755 uint32_t crc_actual = 0; 756 struct msghdr msg; 757 size_t recv_len; 758 759 ASSERT(socket != NULL); 760 ASSERT(ihp != NULL); 761 ASSERT(data != NULL); 762 763 /* short hand dlength */ 764 dlength = ntoh24(ihp->dlength); 765 766 /* verify dlength is valid */ 767 if (dlength > max_data_length) { 768 cmn_err(CE_WARN, "iscsi connection(%p) protocol error - " 769 "invalid data lengths itt:0x%x received:0x%x " 770 "max expected:0x%x", socket, ihp->itt, 771 dlength, max_data_length); 772 return (ISCSI_STATUS_PROTOCOL_ERROR); 773 } 774 775 if (dlength) { 776 /* calculate pad */ 777 pad_len = ((ISCSI_PAD_WORD_LEN - 778 (dlength & (ISCSI_PAD_WORD_LEN - 1))) & 779 (ISCSI_PAD_WORD_LEN - 1)); 780 781 /* setup data iovec */ 782 iov[0].iov_base = (char *)data; 783 iov[0].iov_len = dlength; 784 total_len = dlength; 785 786 /* if pad setup pad iovec */ 787 if (pad_len) { 788 iov[iovlen].iov_base = (char *)&pad; 789 iov[iovlen].iov_len = pad_len; 790 total_len += pad_len; 791 iovlen++; 792 } 793 794 /* setup data digest */ 795 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) { 796 iov[iovlen].iov_base = (char *)&crc_actual; 797 iov[iovlen].iov_len = sizeof (crc_actual); 798 total_len += sizeof (crc_actual); 799 iovlen++; 800 } 801 802 bzero(&msg, sizeof (msg)); 803 msg.msg_iov = iov; 804 msg.msg_flags = MSG_WAITALL; 805 msg.msg_iovlen = iovlen; 806 807 recv_len = iscsi_net->recvmsg(socket, &msg, timeout); 808 if (recv_len != total_len) { 809 return (ISCSI_STATUS_TCP_RX_ERROR); 810 } 811 812 DTRACE_PROBE4(rx_data, void *, socket, iovec_t *iop, 813 &iov[0], int, iovlen, int, total_len); 814 815 /* verify data digest is present */ 816 if ((flags & ISCSI_NET_DATA_DIGEST) != 0) { 817 818 crc_calculated = iscsi_crc32c(data, dlength); 819 crc_calculated = iscsi_crc32c_continued( 820 (char *)&pad, pad_len, crc_calculated); 821 822 /* 823 * Converting actual CRC read via ntohl is not 824 * necessary because iscsi_crc32c calculates the 825 * value as it expects to be read 826 */ 827 if (crc_calculated != crc_actual) { 828 cmn_err(CE_WARN, "iscsi connection(%p) " 829 "protocol error - encountered a data " 830 "digest error itt:0x%x expected:0x%08x " 831 "received:0x%08x", socket, 832 ihp->itt, crc_calculated, crc_actual); 833 return (ISCSI_STATUS_DATA_DIGEST_ERROR); 834 } 835 } 836 } 837 return (ISCSI_STATUS_SUCCESS); 838 } 839 840 /* 841 * Convert a prefix length to a mask. 842 */ 843 static iscsi_status_t 844 iscsi_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask) 845 { 846 if (prefixlen < 0 || prefixlen > maxlen || mask == NULL) { 847 return (ISCSI_STATUS_INTERNAL_ERROR); 848 } 849 850 while (prefixlen > 0) { 851 if (prefixlen >= 8) { 852 *mask = 0xff; 853 mask++; 854 prefixlen = prefixlen - 8; 855 continue; 856 } 857 *mask = *mask | (1 << (8 - prefixlen)); 858 prefixlen--; 859 } 860 return (ISCSI_STATUS_SUCCESS); 861 } 862 863 static iscsi_status_t 864 iscsi_net_interface() 865 { 866 struct in_addr braddr; 867 struct in_addr subnet; 868 struct in_addr myaddr; 869 struct in_addr defgateway; 870 struct in6_addr myaddr6; 871 struct in6_addr subnet6; 872 uchar_t mask_prefix = 0; 873 int mask_bits = 1; 874 TIUSER *tiptr; 875 TIUSER *tiptr6; 876 char ifname[16] = {0}; 877 iscsi_status_t status; 878 879 struct knetconfig dl_udp_netconf = { 880 NC_TPI_CLTS, 881 NC_INET, 882 NC_UDP, 883 0, }; 884 struct knetconfig dl_udp6_netconf = { 885 NC_TPI_CLTS, 886 NC_INET6, 887 NC_UDP, 888 0, }; 889 890 (void) strlcpy(ifname, rootfs.bo_ifname, sizeof (ifname)); 891 892 if (iscsiboot_prop->boot_nic.sin_family == AF_INET) { 893 /* 894 * Assumes only one linkage array element. 895 */ 896 dl_udp_netconf.knc_rdev = 897 makedevice(clone_major, ddi_name_to_major("udp")); 898 899 myaddr.s_addr = 900 iscsiboot_prop->boot_nic.nic_ip_u.u_in4.s_addr; 901 902 mask_prefix = iscsiboot_prop->boot_nic.sub_mask_prefix; 903 (void) memset(&subnet.s_addr, 0, sizeof (subnet)); 904 status = iscsi_prefixlentomask(mask_prefix, IP_4_BITS, 905 (uchar_t *)&subnet.s_addr); 906 if (status != ISCSI_STATUS_SUCCESS) { 907 return (status); 908 } 909 910 mask_bits = mask_bits << (IP_4_BITS - mask_prefix); 911 mask_bits = mask_bits - 1; 912 /* 913 * Set the last mask bits of the ip address with 1, then 914 * we can get the broadcast address. 915 */ 916 braddr.s_addr = myaddr.s_addr | mask_bits; 917 918 defgateway.s_addr = 919 iscsiboot_prop->boot_nic.nic_gw_u.u_in4.s_addr; 920 921 /* initialize interface */ 922 if (t_kopen((file_t *)NULL, dl_udp_netconf.knc_rdev, 923 FREAD|FWRITE, &tiptr, CRED()) == 0) { 924 if (kdlifconfig(tiptr, AF_INET, &myaddr, &subnet, 925 &braddr, &defgateway, ifname)) { 926 cmn_err(CE_WARN, "Failed to configure" 927 " iSCSI boot nic"); 928 (void) t_kclose(tiptr, 0); 929 return (ISCSI_STATUS_INTERNAL_ERROR); 930 } 931 } else { 932 cmn_err(CE_WARN, "Failed to configure" 933 " iSCSI boot nic"); 934 return (ISCSI_STATUS_INTERNAL_ERROR); 935 } 936 return (ISCSI_STATUS_SUCCESS); 937 } else { 938 dl_udp6_netconf.knc_rdev = 939 makedevice(clone_major, ddi_name_to_major("udp6")); 940 941 bcopy(&iscsiboot_prop->boot_nic.nic_ip_u.u_in6.s6_addr, 942 &myaddr6.s6_addr, 16); 943 944 (void) memset(&subnet6, 0, sizeof (subnet6)); 945 mask_prefix = iscsiboot_prop->boot_nic.sub_mask_prefix; 946 status = iscsi_prefixlentomask(mask_prefix, IP_6_BITS, 947 (uchar_t *)&subnet6.s6_addr); 948 if (status != ISCSI_STATUS_SUCCESS) { 949 return (status); 950 } 951 952 if (t_kopen((file_t *)NULL, dl_udp6_netconf.knc_rdev, 953 FREAD|FWRITE, &tiptr6, CRED()) == 0) { 954 if (kdlifconfig(tiptr6, AF_INET6, &myaddr6, 955 &subnet6, NULL, NULL, ifname)) { 956 cmn_err(CE_WARN, "Failed to configure" 957 " iSCSI boot nic"); 958 (void) t_kclose(tiptr, 0); 959 return (ISCSI_STATUS_INTERNAL_ERROR); 960 } 961 } else { 962 cmn_err(CE_WARN, "Failed to configure" 963 " iSCSI boot nic"); 964 return (ISCSI_STATUS_INTERNAL_ERROR); 965 } 966 return (ISCSI_STATUS_SUCCESS); 967 } 968 } 969