1/* $NetBSD: iscsid_driverif.c,v 1.3 2011/11/20 01:23:57 agc Exp $ */ 2 3/*- 4 * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31#include "iscsid_globals.h" 32 33#include <sys/socket.h> 34#include <netinet/in.h> 35#include <netinet/tcp.h> 36#include <netdb.h> 37 38 39/* Global node name (Initiator Name and Alias) */ 40iscsid_set_node_name_req_t node_name; 41 42/* -------------------------------------------------------------------------- */ 43 44/* 45 * set_node_name: 46 * Handle set_node_name request. Copy names into our own buffers and 47 * set the driver's info as well. 48 * 49 * Parameter: 50 * par The request parameter 51 * 52 * Returns: Status. 53 */ 54 55uint32_t 56set_node_name(iscsid_set_node_name_req_t * par) 57{ 58 iscsi_set_node_name_parameters_t snp; 59 60 (void) memset(&snp, 0x0, sizeof(snp)); 61 if (!par->InitiatorName[0]) 62 return ISCSID_STATUS_NO_INITIATOR_NAME; 63 64 if (strlen((char *)par->InitiatorName) > ISCSI_STRING_LENGTH 65 || strlen((char *)par->InitiatorAlias) > ISCSI_STRING_LENGTH) 66 return ISCSID_STATUS_PARAMETER_INVALID; 67 68 if (!par->InitiatorAlias[0]) 69 gethostname((char *)node_name.InitiatorAlias, sizeof(node_name.InitiatorAlias)); 70 71 node_name = *par; 72 73#ifdef ISCSI_DEBUG /* DEBUG ONLY: Allow op without driver present */ 74 if (driver < 0) 75 return ISCSID_STATUS_SUCCESS; 76#endif 77 78 strlcpy((char *)snp.InitiatorName, (char *)par->InitiatorName, 79 sizeof(snp.InitiatorName)); 80 strlcpy((char *)snp.InitiatorAlias, (char *)par->InitiatorAlias, 81 sizeof(snp.InitiatorAlias)); 82 memcpy(snp.ISID, par->ISID, 6); 83 84 DEB(10, ("Setting Node Name: %s (%s)\n", 85 snp.InitiatorName, snp.InitiatorAlias)); 86 (void)ioctl(driver, ISCSI_SET_NODE_NAME, &snp); 87 return snp.status; 88} 89 90 91/* 92 * bind_socket: 93 * Bind socket to initiator portal. 94 * 95 * Parameter: 96 * sock The socket 97 * addr The initiator portal address 98 * 99 * Returns: 100 * TRUE on success, FALSE on error. 101 */ 102 103static int 104bind_socket(int sock, uint8_t * addr) 105{ 106 struct sockaddr_in serverAddress; 107 struct hostent *host; 108 109 DEB(8, ("Binding to <%s>\n", addr)); 110 (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); 111 host = gethostbyname((char *)addr); 112 if (host == NULL) 113 return FALSE; 114 if (host->h_length > (int)sizeof(serverAddress.sin_addr)) 115 return FALSE; 116 serverAddress.sin_family = host->h_addrtype; 117 serverAddress.sin_port = 0; 118 serverAddress.sin_len = host->h_length; 119 memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); 120 121 return bind(sock, (struct sockaddr *)(void *)&serverAddress, 122 (socklen_t)sizeof(serverAddress)) >= 0; 123} 124 125 126/* -------------------------------------------------------------------------- */ 127 128/* 129 * find_free_portal: 130 * Find the Portal with the least number of connections. 131 * 132 * Parameter: the portal group 133 * 134 * Returns: The pointer to the first free portal (or NULL if none found) 135 */ 136 137static portal_t * 138find_free_portal(portal_group_t * group) 139{ 140 portal_t *curr, *m; 141 uint32_t n; 142 143 if ((curr = TAILQ_FIRST(&group->portals)) == NULL) 144 return NULL; 145 146 m = curr; 147 n = curr->active_connections; 148 149 while ((curr = TAILQ_NEXT(curr, group_list)) != NULL) 150 if (curr->active_connections < n) { 151 m = curr; 152 n = curr->active_connections; 153 } 154 155 return m; 156} 157 158 159/* 160 * make_connection: 161 * Common routine for login and add_connection. Creates the connection 162 * structure, connects the socket, and executes the login. 163 * 164 * Parameter: 165 * sess The associated session. NULL for a send_targets request. 166 * req The request parameters. NULL for send_targets. 167 * res The response buffer. For SendTargets, only the status 168 * is set. For a "real" login, the login response 169 * is filled in. 170 * stid Send target request only, else NULL. Pointer to uint32: 171 * On Input, contains send target ID 172 * On Output, receives session ID 173 * 174 * Returns: The connection structure on successful login, else NULL. 175 * 176 * NOTE: Session list must be locked on entry. 177 */ 178 179static connection_t * 180make_connection(session_t * sess, iscsid_login_req_t * req, 181 iscsid_response_t * res, uint32_t * stid) 182{ 183 connection_t *conn; 184 iscsi_login_parameters_t loginp; 185 int sock; 186 int ret; 187 int yes = 1; 188 target_t *target; 189 portal_t *portal = NULL; 190 iscsi_portal_address_t *addr; 191 struct sockaddr_in serverAddress; 192 struct hostent *host; 193 initiator_t *init; 194 195 DEB(9, ("Make Connection sess=%p, req=%p, res=%p, stid=%p\n", 196 sess, req, res, stid)); 197 (void) memset(&loginp, 0x0, sizeof(loginp)); 198 (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); 199 200 /* find the target portal */ 201 if (stid != NULL) { 202 send_target_t *starget; 203 204 if ((starget = find_send_target_id(*stid)) == NULL) { 205 res->status = ISCSID_STATUS_INVALID_TARGET_ID; 206 return NULL; 207 } 208 addr = &starget->addr; 209 target = (target_t *)(void *)starget; 210 } else { 211 if (NO_ID(&req->portal_id) 212 || (portal = find_portal(&req->portal_id)) == NULL) { 213 portal_group_t *group; 214 215 /* if no ID was specified, use target from existing session */ 216 if (NO_ID(&req->portal_id)) { 217 if (!sess->num_connections || 218 ((target = find_target_id(TARGET_LIST, 219 sess->target.sid.id)) == NULL)) { 220 res->status = ISCSID_STATUS_INVALID_PORTAL_ID; 221 return NULL; 222 } 223 } 224 /* if a target was given instead, use it */ 225 else if ((target = 226 find_target(TARGET_LIST, &req->portal_id)) == NULL) { 227 res->status = ISCSID_STATUS_INVALID_PORTAL_ID; 228 return NULL; 229 } 230 /* now get from target to portal - if this is the first connection, */ 231 /* just use the first portal group. */ 232 if (!sess->num_connections) { 233 group = TAILQ_FIRST(&target->group_list); 234 } 235 /* if it's a second connection, use an available portal in the same */ 236 /* portal group */ 237 else { 238 conn = (connection_t *)(void *) 239 TAILQ_FIRST(&sess->connections); 240 241 if (conn == NULL || 242 (portal = find_portal_id(conn->portal.sid.id)) == NULL) { 243 res->status = ISCSID_STATUS_INVALID_PORTAL_ID; 244 return NULL; 245 } 246 group = portal->group; 247 } 248 249 if ((portal = find_free_portal(group)) == NULL) { 250 res->status = ISCSID_STATUS_INVALID_PORTAL_ID; 251 return NULL; 252 } 253 DEB(1, ("find_free_portal returns pid=%d\n", portal->entry.sid.id)); 254 } else 255 target = portal->target; 256 257 addr = &portal->addr; 258 259 /* symbolic name for connection? check for duplicates */ 260 if (req->sym_name[0]) { 261 void *p; 262 263 if (sess->num_connections) 264 p = find_connection_name(sess, req->sym_name); 265 else 266 p = find_session_name(req->sym_name); 267 if (p) { 268 res->status = ISCSID_STATUS_DUPLICATE_NAME; 269 return NULL; 270 } 271 } 272 } 273 274 if (req != NULL && !NO_ID(&req->initiator_id)) { 275 if ((init = find_initiator(&req->initiator_id)) == NULL) { 276 res->status = ISCSID_STATUS_INVALID_INITIATOR_ID; 277 return NULL; 278 } 279 } else 280 init = select_initiator(); 281 282 /* translate target address */ 283 DEB(8, ("Connecting to <%s>, port %d\n", addr->address, addr->port)); 284 285 host = gethostbyname((char *)addr->address); 286 if (host == NULL) { 287 switch (h_errno) { 288 case HOST_NOT_FOUND: 289 res->status = ISCSID_STATUS_HOST_NOT_FOUND; 290 break; 291 case TRY_AGAIN: 292 res->status = ISCSID_STATUS_HOST_TRY_AGAIN; 293 break; 294 default: 295 res->status = ISCSID_STATUS_HOST_ERROR; 296 break; 297 } 298 return NULL; 299 } 300 if (host->h_length > (int)sizeof(serverAddress.sin_addr)) { 301 res->status = ISCSID_STATUS_HOST_ERROR; 302 return NULL; 303 } 304 DEB(8, ("Gethostbyname OK, addrtype %d, len %d, addr %x\n", 305 host->h_addrtype, host->h_length, *((int *) host->h_addr_list[0]))); 306 serverAddress.sin_family = host->h_addrtype; 307 serverAddress.sin_port = htons((addr->port) 308 ? addr->port : ISCSI_DEFAULT_PORT); 309 serverAddress.sin_len = host->h_length; 310 memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); 311 312 /* alloc the connection structure */ 313 conn = calloc(1, sizeof(*conn)); 314 if (conn == NULL) { 315 res->status = ISCSID_STATUS_NO_RESOURCES; 316 return NULL; 317 } 318 /* create and connect the socket */ 319 sock = socket(AF_INET, SOCK_STREAM, 0); 320 if (sock < 0) { 321 free(conn); 322 res->status = ISCSID_STATUS_SOCKET_ERROR; 323 return NULL; 324 } 325 326 if (init) { 327 if (!bind_socket(sock, init->address)) { 328 close(sock); 329 free(conn); 330 res->status = ISCSID_STATUS_INITIATOR_BIND_ERROR; 331 return NULL; 332 } 333 } 334 335 DEB(8, ("Connecting socket\n")); 336 if (connect(sock, (struct sockaddr *)(void *)&serverAddress, 337 (socklen_t)sizeof(serverAddress)) < 0) { 338 close(sock); 339 free(conn); 340 res->status = ISCSID_STATUS_CONNECT_ERROR; 341 DEB(1, ("Connecting to socket failed (error %d), returning %d\n", 342 errno, res->status)); 343 return NULL; 344 } 345 /* speed up socket processing */ 346 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes)); 347 348 /* setup login parameter structure */ 349 loginp.socket = sock; 350 if (target->TargetName[0]) { 351 loginp.is_present.TargetName = 1; 352 loginp.TargetName = target->TargetName; 353 } 354 if (target->options.is_present.MaxConnections) { 355 loginp.is_present.MaxConnections = 1; 356 loginp.MaxConnections = target->options.MaxConnections; 357 } 358 if (target->options.is_present.DataDigest) { 359 loginp.is_present.DataDigest = 1; 360 loginp.DataDigest = target->options.DataDigest; 361 } 362 if (target->options.is_present.HeaderDigest) { 363 loginp.is_present.HeaderDigest = 1; 364 loginp.HeaderDigest = target->options.HeaderDigest; 365 } 366 if (target->options.is_present.DefaultTime2Retain) { 367 loginp.is_present.DefaultTime2Retain = 1; 368 loginp.DefaultTime2Retain = target->options.DefaultTime2Retain; 369 } 370 if (target->options.is_present.DefaultTime2Wait) { 371 loginp.is_present.DefaultTime2Wait = 1; 372 loginp.DefaultTime2Wait = target->options.DefaultTime2Wait; 373 } 374 if (target->options.is_present.ErrorRecoveryLevel) { 375 loginp.is_present.ErrorRecoveryLevel = 1; 376 loginp.ErrorRecoveryLevel = target->options.ErrorRecoveryLevel; 377 } 378 if (target->options.is_present.MaxRecvDataSegmentLength) { 379 loginp.is_present.MaxRecvDataSegmentLength = 1; 380 loginp.MaxRecvDataSegmentLength = 381 target->options.MaxRecvDataSegmentLength; 382 } 383 if (target->auth.auth_info.auth_number) { 384 loginp.is_present.auth_info = 1; 385 loginp.auth_info = target->auth.auth_info; 386 if (target->auth.password[0]) { 387 loginp.is_present.password = 1; 388 loginp.password = target->auth.password; 389 } 390 if (target->auth.target_password[0]) { 391 loginp.is_present.target_password = 1; 392 loginp.target_password = target->auth.target_password; 393 } 394 if (target->auth.user_name[0]) { 395 loginp.is_present.user_name = 1; 396 loginp.user_name = target->auth.user_name; 397 } 398 } 399 loginp.is_present.TargetAlias = 1; 400 loginp.TargetAlias = target->TargetAlias; 401 402 if (portal != NULL) { 403 /* override general target options with portal options (if specified) */ 404 if (portal->options.is_present.DataDigest) { 405 loginp.is_present.DataDigest = 1; 406 loginp.DataDigest = portal->options.DataDigest; 407 } 408 if (portal->options.is_present.HeaderDigest) { 409 loginp.is_present.HeaderDigest = 1; 410 loginp.HeaderDigest = portal->options.HeaderDigest; 411 } 412 if (portal->options.is_present.MaxRecvDataSegmentLength) { 413 loginp.is_present.MaxRecvDataSegmentLength = 1; 414 loginp.MaxRecvDataSegmentLength = 415 portal->options.MaxRecvDataSegmentLength; 416 } 417 } 418 419 if (req != NULL) { 420 loginp.session_id = get_id(&list[SESSION_LIST].list, &req->session_id); 421 loginp.login_type = req->login_type; 422 } else 423 loginp.login_type = ISCSI_LOGINTYPE_DISCOVERY; 424 425 DEB(5, ("Calling Login...\n")); 426 427 ret = ioctl(driver, (sess != NULL && sess->num_connections) 428 ? ISCSI_ADD_CONNECTION : ISCSI_LOGIN, &loginp); 429 430 res->status = loginp.status; 431 432 if (ret) 433 close(sock); 434 435 if (ret || loginp.status) { 436 free(conn); 437 if (!res->status) 438 res->status = ISCSID_STATUS_GENERAL_ERROR; 439 return NULL; 440 } 441 /* connection established! link connection into session and return IDs */ 442 443 conn->loginp = loginp; 444 conn->entry.sid.id = loginp.connection_id; 445 if (req != NULL) { 446 strlcpy((char *)conn->entry.sid.name, (char *)req->sym_name, 447 sizeof(conn->entry.sid.name)); 448 } 449 450 /* 451 Copy important target information 452 */ 453 conn->target.sid = target->entry.sid; 454 strlcpy((char *)conn->target.TargetName, (char *)target->TargetName, 455 sizeof(conn->target.TargetName)); 456 strlcpy((char *)conn->target.TargetAlias, (char *)target->TargetAlias, 457 sizeof(conn->target.TargetAlias)); 458 conn->target.options = target->options; 459 conn->target.auth = target->auth; 460 conn->portal.addr = *addr; 461 462 conn->session = sess; 463 464 if (stid == NULL) { 465 iscsid_login_rsp_t *rsp = (iscsid_login_rsp_t *)(void *) 466 res->parameter; 467 468 sess->entry.sid.id = loginp.session_id; 469 TAILQ_INSERT_TAIL(&sess->connections, &conn->entry, link); 470 sess->num_connections++; 471 472 res->parameter_length = sizeof(*rsp); 473 rsp->connection_id = conn->entry.sid; 474 rsp->session_id = sess->entry.sid; 475 476 if (init != NULL) { 477 conn->initiator_id = init->entry.sid.id; 478 init->active_connections++; 479 } 480 } else 481 *stid = loginp.session_id; 482 483 /* 484 Copy important portal information 485 */ 486 if (portal != NULL) { 487 conn->portal.sid = portal->entry.sid; 488 portal->active_connections++; 489 } 490 491 return conn; 492} 493 494 495/* 496 * event_recover_connection: 497 * Handle RECOVER_CONNECTION event: Attempt to re-establish connection. 498 * 499 * Parameter: 500 * sid Session ID 501 * cid Connection ID 502 */ 503 504static void 505event_recover_connection(uint32_t sid, uint32_t cid) 506{ 507 int sock, ret; 508 int yes = 1; 509 session_t *sess; 510 connection_t *conn; 511 portal_t *portal; 512 initiator_t *init; 513 iscsi_portal_address_t *addr; 514 struct sockaddr_in serverAddress; 515 struct hostent *host; 516 517 DEB(1, ("Event_Recover_Connection sid=%d, cid=%d\n", sid, cid)); 518 (void) memset(&serverAddress, 0x0, sizeof(serverAddress)); 519 520 LOCK_SESSIONS; 521 522 sess = find_session_id(sid); 523 if (sess == NULL) { 524 UNLOCK_SESSIONS; 525 return; 526 } 527 528 conn = find_connection_id(sess, cid); 529 if (conn == NULL) { 530 UNLOCK_SESSIONS; 531 return; 532 } 533 534 UNLOCK_SESSIONS; 535 536 conn->loginp.status = ISCSI_STATUS_CONNECTION_FAILED; 537 538 /* If we can't find the portal to connect to, abort. */ 539 540 if ((portal = find_portal_id(conn->portal.sid.id)) == NULL) 541 return; 542 543 init = find_initiator_id(conn->initiator_id); 544 addr = &portal->addr; 545 conn->portal.addr = *addr; 546 547 /* translate target address */ 548 DEB(1, ("Event_Recover_Connection Connecting to <%s>, port %d\n", 549 addr->address, addr->port)); 550 551 if ((host = gethostbyname((char *)addr->address)) == NULL) { 552 DEB(1, ("GetHostByName failed (error %d)\n", h_errno)); 553 return; 554 } 555 if (host->h_length > (int)sizeof(serverAddress.sin_addr)) { 556 DEB(1, ("Host address length invalid (%d)\n", host->h_length)); 557 return; 558 } 559 560 serverAddress.sin_family = host->h_addrtype; 561 serverAddress.sin_port = htons((addr->port) 562 ? addr->port : ISCSI_DEFAULT_PORT); 563 serverAddress.sin_len = host->h_length; 564 memcpy(&serverAddress.sin_addr, host->h_addr_list[0], host->h_length); 565 566 /* create and connect the socket */ 567 sock = socket(AF_INET, SOCK_STREAM, 0); 568 if (sock < 0) { 569 DEB(1, ("Creating socket failed (error %d)\n", errno)); 570 return; 571 } 572 573 DEB(1, ("recover_connection: Socket = %d\n", sock)); 574 575 if (init) { 576 if (!bind_socket(sock, init->address)) { 577 DEB(1, ("Binding to interface failed (error %d)\n", errno)); 578 close(sock); 579 return; 580 } 581 } 582 583 if (connect(sock, (struct sockaddr *)(void *)&serverAddress, 584 (socklen_t)sizeof(serverAddress)) < 0) { 585 DEB(1, ("Connecting to socket failed (error %d)\n", errno)); 586 close(sock); 587 return; 588 } 589 /* speed up socket processing */ 590 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(yes)); 591 592 conn->loginp.socket = sock; 593 conn->loginp.status = 0; 594 ret = ioctl(driver, ISCSI_RESTORE_CONNECTION, &conn->loginp); 595 596 if (ret) 597 close(sock); 598} 599 600 601/* 602 * login: 603 * Handle LOGIN request: Log into given portal. Create session, then 604 * let make_connection do the rest. 605 * 606 * Parameter: 607 * req The request parameters 608 * res The response buffer 609 */ 610 611void 612login(iscsid_login_req_t * req, iscsid_response_t * res) 613{ 614 session_t *sess; 615 connection_t *conn; 616 617 sess = calloc(1, sizeof(*sess)); 618 if (sess == NULL) { 619 res->status = ISCSID_STATUS_NO_RESOURCES; 620 return; 621 } 622 TAILQ_INIT(&sess->connections); 623 strlcpy((char *)sess->entry.sid.name, (char *)req->sym_name, 624 sizeof(sess->entry.sid.name)); 625 626 LOCK_SESSIONS; 627 conn = make_connection(sess, req, res, 0); 628 629 if (conn == NULL) 630 free(sess); 631 else { 632 sess->target = conn->target; 633 TAILQ_INSERT_TAIL(&list[SESSION_LIST].list, &sess->entry, link); 634 list[SESSION_LIST].num_entries++; 635 } 636 UNLOCK_SESSIONS; 637} 638 639 640/* 641 * add_connection: 642 * Handle ADD_CONNECTION request: Log secondary connection into given portal. 643 * Find the session, then let make_connection do the rest. 644 * 645 * Parameter: 646 * req The request parameters 647 * res The response buffer 648 */ 649 650void 651add_connection(iscsid_login_req_t * req, iscsid_response_t * res) 652{ 653 session_t *sess; 654 655 LOCK_SESSIONS; 656 sess = find_session(&req->session_id); 657 if (sess == NULL) { 658 UNLOCK_SESSIONS; 659 res->status = ISCSID_STATUS_INVALID_SESSION_ID; 660 return; 661 } 662 663 make_connection(sess, req, res, 0); 664 UNLOCK_SESSIONS; 665} 666 667 668/* 669 * logout: 670 * Handle LOGOUT request: Log out the given session. 671 * 672 * Parameter: 673 * req The request parameters 674 * 675 * Returns: Response status 676 */ 677 678uint32_t 679logout(iscsid_sym_id_t * req) 680{ 681 iscsi_logout_parameters_t logoutp; 682 session_t *sess; 683 int ret; 684 685 (void) memset(&logoutp, 0x0, sizeof(logoutp)); 686 LOCK_SESSIONS; 687 sess = find_session(req); 688 if (sess == NULL) { 689 UNLOCK_SESSIONS; 690 return ISCSID_STATUS_INVALID_SESSION_ID; 691 } 692 693 logoutp.session_id = sess->entry.sid.id; 694 UNLOCK_SESSIONS; 695 696 ret = ioctl(driver, ISCSI_LOGOUT, &logoutp); 697 DEB(9, ("Logout returns %d, status = %d\n", ret, logoutp.status)); 698 699 return logoutp.status; 700} 701 702 703/* 704 * remove_connection: 705 * Handle REMOVE_CONNECTION request: Log out the given connection. 706 * 707 * Parameter: 708 * req The request parameters 709 * 710 * Returns: Response status 711 */ 712 713uint32_t 714remove_connection(iscsid_remove_connection_req_t * req) 715{ 716 iscsi_remove_parameters_t removep; 717 session_t *sess; 718 connection_t *conn; 719 int ret; 720 721 LOCK_SESSIONS; 722 sess = find_session(&req->session_id); 723 if (sess == NULL) { 724 UNLOCK_SESSIONS; 725 return ISCSID_STATUS_INVALID_SESSION_ID; 726 } 727 conn = find_connection(sess, &req->connection_id); 728 if (conn == NULL) { 729 UNLOCK_SESSIONS; 730 return ISCSID_STATUS_INVALID_CONNECTION_ID; 731 } 732 733 removep.session_id = sess->entry.sid.id; 734 removep.connection_id = conn->entry.sid.id; 735 UNLOCK_SESSIONS; 736 737 ret = ioctl(driver, ISCSI_REMOVE_CONNECTION, &removep); 738 DEB(9, ("Remove Connection returns %d, status=%d\n", ret, removep.status)); 739 740 return removep.status; 741} 742 743/* 744 * send_targets: 745 * Handle SEND_TARGETS request: 746 * First login with type = discovery. 747 * Then send the SendTargets iSCSI request to the target, which will 748 * return a list of target portals. 749 * Then logout. 750 * 751 * Parameter: 752 * stid The send target ID 753 * response_buffer Pointer to pointer to buffer containing response 754 * The response contains the list of the target 755 * portals. The caller frees the buffer after it 756 * is done with it. 757 * response_size Pointer to variable which upon return will hold 758 * the size of the response buffer. 759 * 760 * Returns: Response status 761 */ 762 763uint32_t 764send_targets(uint32_t stid, uint8_t **response_buffer, uint32_t *response_size) 765{ 766 iscsi_send_targets_parameters_t sendt; 767 iscsi_logout_parameters_t logoutp; 768 int ret; 769 connection_t *conn; 770 iscsid_response_t res; 771 uint32_t rc = ISCSID_STATUS_SUCCESS; 772 773 (void) memset(&sendt, 0x0, sizeof(sendt)); 774 (void) memset(&logoutp, 0x0, sizeof(logoutp)); 775 (void) memset(&res, 0x0, sizeof(res)); 776 conn = make_connection(NULL, NULL, &res, &stid); 777 DEB(9, ("Make connection returns, status = %d\n", res.status)); 778 779 if (conn == NULL) 780 return res.status; 781 782 sendt.session_id = stid; 783 sendt.response_buffer = NULL; 784 sendt.response_size = 0; 785 sendt.response_used = sendt.response_total = 0; 786 strlcpy((char *)sendt.key, "All", sizeof(sendt.key)); 787 788 /*Call once to get the size of the buffer necessary */ 789 ret = ioctl(driver, ISCSI_SEND_TARGETS, &sendt); 790 791 if (!ret && !sendt.status) { 792 /* Allocate buffer required and call again to retrieve data */ 793 /* We allocate one extra byte so we can place a terminating 0 */ 794 /* at the end of the buffer. */ 795 796 sendt.response_size = sendt.response_total; 797 sendt.response_buffer = calloc(1, sendt.response_size + 1); 798 if (sendt.response_buffer == NULL) 799 rc = ISCSID_STATUS_NO_RESOURCES; 800 else { 801 ret = ioctl(driver, ISCSI_SEND_TARGETS, &sendt); 802 ((uint8_t *)sendt.response_buffer)[sendt.response_size] = 0; 803 804 if (ret || sendt.status) { 805 free(sendt.response_buffer); 806 sendt.response_buffer = NULL; 807 sendt.response_used = 0; 808 if ((rc = sendt.status) == 0) 809 rc = ISCSID_STATUS_GENERAL_ERROR; 810 } 811 } 812 } else if ((rc = sendt.status) == 0) 813 rc = ISCSID_STATUS_GENERAL_ERROR; 814 815 *response_buffer = sendt.response_buffer; 816 *response_size = sendt.response_used; 817 818 logoutp.session_id = stid; 819 ret = ioctl(driver, ISCSI_LOGOUT, &logoutp); 820 /* ignore logout status */ 821 822 free(conn); 823 824 return rc; 825} 826 827 828 829/* 830 * get_version: 831 * Handle GET_VERSION request. 832 * 833 * Returns: Filled get_version_rsp structure. 834 */ 835 836void 837get_version(iscsid_response_t ** prsp, int *prsp_temp) 838{ 839 iscsid_response_t *rsp = *prsp; 840 iscsid_get_version_rsp_t *ver; 841 iscsi_get_version_parameters_t drv_ver; 842 843 rsp = make_rsp(sizeof(iscsid_get_version_rsp_t), prsp, prsp_temp); 844 if (rsp == NULL) 845 return; 846 ver = (iscsid_get_version_rsp_t *)(void *)rsp->parameter; 847 848 ver->interface_version = INTERFACE_VERSION; 849 ver->major = VERSION_MAJOR; 850 ver->minor = VERSION_MINOR; 851 strlcpy ((char *)ver->version_string, VERSION_STRING, sizeof(ver->version_string)); 852 853#ifdef ISCSI_DEBUG /* DEBUG ONLY: Allow op without driver present */ 854 if (driver < 0) 855 return; 856#endif 857 ioctl(driver, ISCSI_GET_VERSION, &drv_ver); 858 ver->driver_interface_version = drv_ver.interface_version; 859 ver->driver_major = drv_ver.major; 860 ver->driver_minor = drv_ver.minor; 861 strlcpy ((char *)ver->driver_version_string, (char *)drv_ver.version_string, 862 sizeof (ver->driver_version_string)); 863} 864 865 866/* -------------------------------------------------------------------------- */ 867 868iscsi_register_event_parameters_t event_reg; /* registered event ID */ 869 870 871/* 872 * register_event_handler: 873 * Call driver to register the event handler. 874 * 875 * Returns: 876 * TRUE on success. 877 */ 878 879boolean_t 880register_event_handler(void) 881{ 882 ioctl(driver, ISCSI_REGISTER_EVENT, &event_reg); 883 return event_reg.event_id != 0; 884} 885 886 887/* 888 * deregister_event_handler: 889 * Call driver to deregister the event handler. If the event handler thread 890 * is waiting for an event, this will wake it up and cause it to exit. 891 */ 892 893void 894deregister_event_handler(void) 895{ 896 if (event_reg.event_id) { 897 ioctl(driver, ISCSI_DEREGISTER_EVENT, &event_reg); 898 event_reg.event_id = 0; 899 } 900} 901 902 903/* 904 * event_handler: 905 * Event handler thread. Wait for the driver to generate an event and 906 * process it appropriately. Exits when the driver terminates or the 907 * handler is deregistered because the daemon is terminating. 908 * 909 * Parameter: 910 * par Not used. 911 */ 912 913void * 914/*ARGSUSED*/ 915event_handler(void *par) 916{ 917 iscsi_wait_event_parameters_t evtp; 918 int rc; 919 920 DEB(99, ("Event handler starts\n")); 921 (void) memset(&evtp, 0x0, sizeof(evtp)); 922 923 evtp.event_id = event_reg.event_id; 924 925 do { 926 if (nothreads) 927 rc = ioctl(driver, ISCSI_POLL_EVENT, &evtp); 928 else 929 rc = ioctl(driver, ISCSI_WAIT_EVENT, &evtp); 930 if (rc || evtp.status) 931 break; 932 933 DEB(1, ("Got Event: kind %d, status %d, sid %d, cid %d, reason %d\n", 934 evtp.event_kind, evtp.status, evtp.session_id, 935 evtp.connection_id, evtp.reason)); 936 937 switch (evtp.event_kind) { 938 case ISCSI_SESSION_TERMINATED: 939 event_kill_session(evtp.session_id); 940 break; 941 942 case ISCSI_CONNECTION_TERMINATED: 943 event_kill_connection(evtp.session_id, evtp.connection_id); 944 break; 945 946 case ISCSI_RECOVER_CONNECTION: 947 event_recover_connection(evtp.session_id, evtp.connection_id); 948 break; 949 950 default: 951 break; 952 } 953 } while (evtp.event_kind != ISCSI_DRIVER_TERMINATING); 954 955 if (nothreads && evtp.event_kind == ISCSI_DRIVER_TERMINATING) 956 exit_daemon(); 957 958 return NULL; 959} 960 961#if 0 962/* 963 * verify_connection: 964 * Verify that a specific connection still exists, delete it if not. 965 * 966 * Parameter: The connection pointer. 967 * 968 * Returns: The status returned by the driver. 969 */ 970 971uint32_t 972verify_connection(connection_t * conn) 973{ 974 iscsi_conn_status_parameters_t req; 975 session_t *sess = conn->session; 976 977 req.connection_id = conn->entry.sid.id; 978 req.session_id = sess->entry.sid.id; 979 980 ioctl(driver, ISCSI_CONNECTION_STATUS, &req); 981 982 if (req.status) { 983 TAILQ_REMOVE(&sess->connections, &conn->entry, link); 984 sess->num_connections--; 985 free(conn); 986 } 987 DEB(9, ("Verify connection returns status %d\n", req.status)); 988 return req.status; 989} 990 991#endif 992