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