iscsid.c revision 255665
1/*- 2 * Copyright (c) 2012 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/usr.sbin/iscsid/iscsid.c 255665 2013-09-18 08:37:14Z trasz $ 30 */ 31 32#include <sys/types.h> 33#include <sys/time.h> 34#include <sys/ioctl.h> 35#include <sys/param.h> 36#include <sys/linker.h> 37#include <sys/socket.h> 38#include <sys/capability.h> 39#include <sys/wait.h> 40#include <assert.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <netdb.h> 44#include <signal.h> 45#include <stdbool.h> 46#include <stdint.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <unistd.h> 51 52#include <libutil.h> 53 54#include "iscsid.h" 55 56static volatile bool sigalrm_received = false; 57 58static int nchildren = 0; 59 60static void 61usage(void) 62{ 63 64 fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); 65 exit(1); 66} 67 68char * 69checked_strdup(const char *s) 70{ 71 char *c; 72 73 c = strdup(s); 74 if (c == NULL) 75 log_err(1, "strdup"); 76 return (c); 77} 78 79static void 80resolve_addr(const struct connection *conn, const char *address, 81 struct addrinfo **ai, bool initiator_side) 82{ 83 struct addrinfo hints; 84 char *arg, *addr, *ch; 85 const char *port; 86 int error, colons = 0; 87 88 arg = checked_strdup(address); 89 90 if (arg[0] == '\0') { 91 fail(conn, "empty address"); 92 log_errx(1, "empty address"); 93 } 94 if (arg[0] == '[') { 95 /* 96 * IPv6 address in square brackets, perhaps with port. 97 */ 98 arg++; 99 addr = strsep(&arg, "]"); 100 if (arg == NULL) { 101 fail(conn, "malformed address"); 102 log_errx(1, "malformed address %s", address); 103 } 104 if (arg[0] == '\0') { 105 port = NULL; 106 } else if (arg[0] == ':') { 107 port = arg + 1; 108 } else { 109 fail(conn, "malformed address"); 110 log_errx(1, "malformed address %s", address); 111 } 112 } else { 113 /* 114 * Either IPv6 address without brackets - and without 115 * a port - or IPv4 address. Just count the colons. 116 */ 117 for (ch = arg; *ch != '\0'; ch++) { 118 if (*ch == ':') 119 colons++; 120 } 121 if (colons > 1) { 122 addr = arg; 123 port = NULL; 124 } else { 125 addr = strsep(&arg, ":"); 126 if (arg == NULL) 127 port = NULL; 128 else 129 port = arg; 130 } 131 } 132 133 if (port == NULL && !initiator_side) 134 port = "3260"; 135 136 memset(&hints, 0, sizeof(hints)); 137 hints.ai_family = PF_UNSPEC; 138 hints.ai_socktype = SOCK_STREAM; 139 hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; 140 if (initiator_side) 141 hints.ai_flags |= AI_PASSIVE; 142 143 error = getaddrinfo(addr, port, &hints, ai); 144 if (error != 0) { 145 fail(conn, gai_strerror(error)); 146 log_errx(1, "getaddrinfo for %s failed: %s", 147 address, gai_strerror(error)); 148 } 149} 150 151static struct connection * 152connection_new(unsigned int session_id, const struct iscsi_session_conf *conf, 153 int iscsi_fd) 154{ 155 struct connection *conn; 156 struct addrinfo *from_ai, *to_ai; 157 const char *from_addr, *to_addr; 158#ifdef ICL_KERNEL_PROXY 159 struct iscsi_daemon_connect *idc; 160#endif 161 int error; 162 163 conn = calloc(1, sizeof(*conn)); 164 if (conn == NULL) 165 log_err(1, "calloc"); 166 167 /* 168 * Default values, from RFC 3720, section 12. 169 */ 170 conn->conn_header_digest = CONN_DIGEST_NONE; 171 conn->conn_data_digest = CONN_DIGEST_NONE; 172 conn->conn_initial_r2t = true; 173 conn->conn_immediate_data = true; 174 conn->conn_max_data_segment_length = 8192; 175 conn->conn_max_burst_length = 262144; 176 conn->conn_first_burst_length = 65536; 177 178 conn->conn_session_id = session_id; 179 conn->conn_iscsi_fd = iscsi_fd; 180 181 /* 182 * XXX: Should we sanitize this somehow? 183 */ 184 memcpy(&conn->conn_conf, conf, sizeof(conn->conn_conf)); 185 186 from_addr = conn->conn_conf.isc_initiator_addr; 187 to_addr = conn->conn_conf.isc_target_addr; 188 189 if (from_addr[0] != '\0') 190 resolve_addr(conn, from_addr, &from_ai, true); 191 else 192 from_ai = NULL; 193 194 resolve_addr(conn, to_addr, &to_ai, false); 195 196#ifdef ICL_KERNEL_PROXY 197 198 idc = calloc(1, sizeof(*idc)); 199 if (idc == NULL) 200 log_err(1, "calloc"); 201 202 idc->idc_session_id = conn->conn_session_id; 203 if (conn->conn_conf.isc_iser) 204 idc->idc_iser = 1; 205 idc->idc_domain = to_ai->ai_family; 206 idc->idc_socktype = to_ai->ai_socktype; 207 idc->idc_protocol = to_ai->ai_protocol; 208 if (from_ai != NULL) { 209 idc->idc_from_addr = from_ai->ai_addr; 210 idc->idc_from_addrlen = from_ai->ai_addrlen; 211 } 212 idc->idc_to_addr = to_ai->ai_addr; 213 idc->idc_to_addrlen = to_ai->ai_addrlen; 214 215 log_debugx("connecting to %s using ICL kernel proxy", to_addr); 216 error = ioctl(iscsi_fd, ISCSIDCONNECT, idc); 217 if (error != 0) { 218 fail(conn, strerror(errno)); 219 log_err(1, "failed to connect to %s using ICL kernel proxy", 220 to_addr); 221 } 222 223#else /* !ICL_KERNEL_PROXY */ 224 225 if (conn->conn_conf.isc_iser) { 226 fail(conn, "iSER not supported"); 227 log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " 228 "does not support iSER"); 229 } 230 231 conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, 232 to_ai->ai_protocol); 233 if (conn->conn_socket < 0) { 234 fail(conn, strerror(errno)); 235 log_err(1, "failed to create socket for %s", from_addr); 236 } 237 if (from_ai != NULL) { 238 error = bind(conn->conn_socket, from_ai->ai_addr, 239 from_ai->ai_addrlen); 240 if (error != 0) { 241 fail(conn, strerror(errno)); 242 log_err(1, "failed to bind to %s", from_addr); 243 } 244 } 245 log_debugx("connecting to %s", to_addr); 246 error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); 247 if (error != 0) { 248 fail(conn, strerror(errno)); 249 log_err(1, "failed to connect to %s", to_addr); 250 } 251 252#endif /* !ICL_KERNEL_PROXY */ 253 254 return (conn); 255} 256 257static void 258handoff(struct connection *conn) 259{ 260 struct iscsi_daemon_handoff *idh; 261 int error; 262 263 log_debugx("handing off connection to the kernel"); 264 265 idh = calloc(1, sizeof(*idh)); 266 if (idh == NULL) 267 log_err(1, "calloc"); 268 idh->idh_session_id = conn->conn_session_id; 269#ifndef ICL_KERNEL_PROXY 270 idh->idh_socket = conn->conn_socket; 271#endif 272 strlcpy(idh->idh_target_alias, conn->conn_target_alias, 273 sizeof(idh->idh_target_alias)); 274 memcpy(idh->idh_isid, conn->conn_isid, sizeof(idh->idh_isid)); 275 idh->idh_statsn = conn->conn_statsn; 276 idh->idh_header_digest = conn->conn_header_digest; 277 idh->idh_data_digest = conn->conn_data_digest; 278 idh->idh_initial_r2t = conn->conn_initial_r2t; 279 idh->idh_immediate_data = conn->conn_immediate_data; 280 idh->idh_max_data_segment_length = conn->conn_max_data_segment_length; 281 idh->idh_max_burst_length = conn->conn_max_burst_length; 282 idh->idh_first_burst_length = conn->conn_first_burst_length; 283 284 error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, idh); 285 if (error != 0) 286 log_err(1, "ISCSIDHANDOFF"); 287} 288 289void 290fail(const struct connection *conn, const char *reason) 291{ 292 struct iscsi_daemon_fail *idf; 293 int error; 294 295 idf = calloc(1, sizeof(*idf)); 296 if (idf == NULL) 297 log_err(1, "calloc"); 298 299 idf->idf_session_id = conn->conn_session_id; 300 strlcpy(idf->idf_reason, reason, sizeof(idf->idf_reason)); 301 302 error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, idf); 303 if (error != 0) 304 log_err(1, "ISCSIDFAIL"); 305} 306 307/* 308 * XXX: I CANT INTO LATIN 309 */ 310static void 311capsicate(struct connection *conn) 312{ 313 int error; 314 cap_rights_t rights; 315#ifdef ICL_KERNEL_PROXY 316 const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, 317 ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE }; 318#else 319 const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, 320 ISCSISREMOVE }; 321#endif 322 323 cap_rights_init(&rights, CAP_IOCTL); 324 error = cap_rights_limit(conn->conn_iscsi_fd, &rights); 325 if (error != 0 && errno != ENOSYS) 326 log_err(1, "cap_rights_limit"); 327 328 error = cap_ioctls_limit(conn->conn_iscsi_fd, cmds, 329 sizeof(cmds) / sizeof(cmds[0])); 330 if (error != 0 && errno != ENOSYS) 331 log_err(1, "cap_ioctls_limit"); 332 333 error = cap_enter(); 334 if (error != 0 && errno != ENOSYS) 335 log_err(1, "cap_enter"); 336 337 if (cap_sandboxed()) 338 log_debugx("Capsicum capability mode enabled"); 339 else 340 log_warnx("Capsicum capability mode not supported"); 341} 342 343bool 344timed_out(void) 345{ 346 347 return (sigalrm_received); 348} 349 350static void 351sigalrm_handler(int dummy __unused) 352{ 353 /* 354 * It would be easiest to just log an error and exit. We can't 355 * do this, though, because log_errx() is not signal safe, since 356 * it calls syslog(3). Instead, set a flag checked by pdu_send() 357 * and pdu_receive(), to call log_errx() there. Should they fail 358 * to notice, we'll exit here one second later. 359 */ 360 if (sigalrm_received) { 361 /* 362 * Oh well. Just give up and quit. 363 */ 364 _exit(2); 365 } 366 367 sigalrm_received = true; 368} 369 370static void 371set_timeout(int timeout) 372{ 373 struct sigaction sa; 374 struct itimerval itv; 375 int error; 376 377 if (timeout <= 0) { 378 log_debugx("session timeout disabled"); 379 return; 380 } 381 382 bzero(&sa, sizeof(sa)); 383 sa.sa_handler = sigalrm_handler; 384 sigfillset(&sa.sa_mask); 385 error = sigaction(SIGALRM, &sa, NULL); 386 if (error != 0) 387 log_err(1, "sigaction"); 388 389 /* 390 * First SIGALRM will arive after conf_timeout seconds. 391 * If we do nothing, another one will arrive a second later. 392 */ 393 bzero(&itv, sizeof(itv)); 394 itv.it_interval.tv_sec = 1; 395 itv.it_value.tv_sec = timeout; 396 397 log_debugx("setting session timeout to %d seconds", 398 timeout); 399 error = setitimer(ITIMER_REAL, &itv, NULL); 400 if (error != 0) 401 log_err(1, "setitimer"); 402} 403 404static void 405handle_request(int iscsi_fd, struct iscsi_daemon_request *request, int timeout) 406{ 407 struct connection *conn; 408 409 log_set_peer_addr(request->idr_conf.isc_target_addr); 410 if (request->idr_conf.isc_target[0] != '\0') { 411 log_set_peer_name(request->idr_conf.isc_target); 412 setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); 413 } else { 414 setproctitle("%s", request->idr_conf.isc_target_addr); 415 } 416 417 conn = connection_new(request->idr_session_id, &request->idr_conf, iscsi_fd); 418 set_timeout(timeout); 419 capsicate(conn); 420 login(conn); 421 if (conn->conn_conf.isc_discovery != 0) 422 discovery(conn); 423 else 424 handoff(conn); 425 426 log_debugx("nothing more to do; exiting"); 427 exit (0); 428} 429 430static int 431wait_for_children(bool block) 432{ 433 pid_t pid; 434 int status; 435 int num = 0; 436 437 for (;;) { 438 /* 439 * If "block" is true, wait for at least one process. 440 */ 441 if (block && num == 0) 442 pid = wait4(-1, &status, 0, NULL); 443 else 444 pid = wait4(-1, &status, WNOHANG, NULL); 445 if (pid <= 0) 446 break; 447 if (WIFSIGNALED(status)) { 448 log_warnx("child process %d terminated with signal %d", 449 pid, WTERMSIG(status)); 450 } else if (WEXITSTATUS(status) != 0) { 451 log_warnx("child process %d terminated with exit status %d", 452 pid, WEXITSTATUS(status)); 453 } else { 454 log_debugx("child process %d terminated gracefully", pid); 455 } 456 num++; 457 } 458 459 return (num); 460} 461 462int 463main(int argc, char **argv) 464{ 465 int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, 466 timeout = 60; 467 bool dont_daemonize = false; 468 struct pidfh *pidfh; 469 pid_t pid, otherpid; 470 const char *pidfile_path = DEFAULT_PIDFILE; 471 struct iscsi_daemon_request *request; 472 473 while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { 474 switch (ch) { 475 case 'P': 476 pidfile_path = optarg; 477 break; 478 case 'd': 479 dont_daemonize = true; 480 debug++; 481 break; 482 case 'l': 483 debug = atoi(optarg); 484 break; 485 case 'm': 486 maxproc = atoi(optarg); 487 break; 488 case 't': 489 timeout = atoi(optarg); 490 break; 491 case '?': 492 default: 493 usage(); 494 } 495 } 496 argc -= optind; 497 if (argc != 0) 498 usage(); 499 500 log_init(debug); 501 502 pidfh = pidfile_open(pidfile_path, 0600, &otherpid); 503 if (pidfh == NULL) { 504 if (errno == EEXIST) 505 log_errx(1, "daemon already running, pid: %jd.", 506 (intmax_t)otherpid); 507 log_err(1, "cannot open or create pidfile \"%s\"", 508 pidfile_path); 509 } 510 511 iscsi_fd = open(ISCSI_PATH, O_RDWR); 512 if (iscsi_fd < 0 && errno == ENOENT) { 513 saved_errno = errno; 514 retval = kldload("iscsi"); 515 if (retval != -1) 516 iscsi_fd = open(ISCSI_PATH, O_RDWR); 517 else 518 errno = saved_errno; 519 } 520 if (iscsi_fd < 0) 521 log_err(1, "failed to open %s", ISCSI_PATH); 522 523 if (dont_daemonize == false) { 524 if (daemon(0, 0) == -1) { 525 log_warn("cannot daemonize"); 526 pidfile_remove(pidfh); 527 exit(1); 528 } 529 } 530 531 pidfile_write(pidfh); 532 533 for (;;) { 534 log_debugx("waiting for request from the kernel"); 535 536 request = calloc(1, sizeof(*request)); 537 if (request == NULL) 538 log_err(1, "calloc"); 539 540 error = ioctl(iscsi_fd, ISCSIDWAIT, request); 541 if (error != 0) { 542 if (errno == EINTR) { 543 nchildren -= wait_for_children(false); 544 assert(nchildren >= 0); 545 continue; 546 } 547 548 log_err(1, "ISCSIDWAIT"); 549 } 550 551 if (dont_daemonize) { 552 log_debugx("not forking due to -d flag; " 553 "will exit after servicing a single request"); 554 } else { 555 nchildren -= wait_for_children(false); 556 assert(nchildren >= 0); 557 558 while (maxproc > 0 && nchildren >= maxproc) { 559 log_debugx("maxproc limit of %d child processes hit; " 560 "waiting for child process to exit", maxproc); 561 nchildren -= wait_for_children(true); 562 assert(nchildren >= 0); 563 } 564 log_debugx("incoming connection; forking child process #%d", 565 nchildren); 566 nchildren++; 567 568 pid = fork(); 569 if (pid < 0) 570 log_err(1, "fork"); 571 if (pid > 0) 572 continue; 573 } 574 575 pidfile_close(pidfh); 576 handle_request(iscsi_fd, request, timeout); 577 } 578 579 return (0); 580} 581