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