1*7b833938Sclaudio /* $OpenBSD: iscsid.c,v 1.25 2025/01/22 16:06:36 claudio Exp $ */ 2bde1ae23Sclaudio 3bde1ae23Sclaudio /* 4bde1ae23Sclaudio * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5bde1ae23Sclaudio * 6bde1ae23Sclaudio * Permission to use, copy, modify, and distribute this software for any 7bde1ae23Sclaudio * purpose with or without fee is hereby granted, provided that the above 8bde1ae23Sclaudio * copyright notice and this permission notice appear in all copies. 9bde1ae23Sclaudio * 10bde1ae23Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11bde1ae23Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12bde1ae23Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13bde1ae23Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14bde1ae23Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15bde1ae23Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16bde1ae23Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17bde1ae23Sclaudio */ 18bde1ae23Sclaudio 19bde1ae23Sclaudio #include <sys/queue.h> 20bde1ae23Sclaudio #include <sys/socket.h> 2183d39657Sclaudio #include <sys/sysctl.h> 22bde1ae23Sclaudio #include <sys/time.h> 23bde1ae23Sclaudio #include <sys/uio.h> 24bde1ae23Sclaudio 25bde1ae23Sclaudio #include <err.h> 26bde1ae23Sclaudio #include <event.h> 27bde1ae23Sclaudio #include <pwd.h> 28bde1ae23Sclaudio #include <signal.h> 29bde1ae23Sclaudio #include <stdio.h> 30bde1ae23Sclaudio #include <stdlib.h> 31bde1ae23Sclaudio #include <string.h> 32bde1ae23Sclaudio #include <unistd.h> 33bde1ae23Sclaudio 34bde1ae23Sclaudio #include "iscsid.h" 35bde1ae23Sclaudio #include "log.h" 36bde1ae23Sclaudio 37bde1ae23Sclaudio void main_sig_handler(int, short, void *); 38bde1ae23Sclaudio __dead void usage(void); 3912bd4d7fSclaudio void shutdown_cb(int, short, void *); 40bde1ae23Sclaudio 4112bd4d7fSclaudio struct event exit_ev; 4212bd4d7fSclaudio int exit_rounds; 4312bd4d7fSclaudio #define ISCSI_EXIT_WAIT 5 44bde1ae23Sclaudio 454125a3c4Sclaudio const struct session_params iscsi_sess_defaults = { 464125a3c4Sclaudio .MaxBurstLength = 262144, 474125a3c4Sclaudio .FirstBurstLength = 65536, 484125a3c4Sclaudio .DefaultTime2Wait = 2, 494125a3c4Sclaudio .DefaultTime2Retain = 20, 504125a3c4Sclaudio .MaxOutstandingR2T = 1, 514125a3c4Sclaudio .MaxConnections = 1, 524125a3c4Sclaudio .InitialR2T = 1, 534125a3c4Sclaudio .ImmediateData = 1, 544125a3c4Sclaudio .DataPDUInOrder = 1, 554125a3c4Sclaudio .DataSequenceInOrder = 1, 56aeec9c16Sclaudio .ErrorRecoveryLevel = 0, 574125a3c4Sclaudio }; 584125a3c4Sclaudio 594125a3c4Sclaudio const struct connection_params iscsi_conn_defaults = { 60aeec9c16Sclaudio .MaxRecvDataSegmentLength = 8192, 61aeec9c16Sclaudio .HeaderDigest = DIGEST_NONE, 62aeec9c16Sclaudio .DataDigest = DIGEST_NONE, 634125a3c4Sclaudio }; 644125a3c4Sclaudio 65bde1ae23Sclaudio int 66bde1ae23Sclaudio main(int argc, char *argv[]) 67bde1ae23Sclaudio { 68bde1ae23Sclaudio struct event ev_sigint, ev_sigterm, ev_sighup; 69bde1ae23Sclaudio struct passwd *pw; 70bde1ae23Sclaudio char *ctrlsock = ISCSID_CONTROL; 71bde1ae23Sclaudio char *vscsidev = ISCSID_DEVICE; 7283d39657Sclaudio int name[] = { CTL_KERN, KERN_PROC_NOBROADCASTKILL, 0 }; 7383d39657Sclaudio int ch, debug = 0, verbose = 0, nobkill = 1; 74bde1ae23Sclaudio 7519200396Sbenno log_procname = getprogname(); 7619200396Sbenno 77bde1ae23Sclaudio log_init(1); /* log to stderr until daemonized */ 78f91d3f97Ssthen log_verbose(1); 79bde1ae23Sclaudio 80f91d3f97Ssthen while ((ch = getopt(argc, argv, "dn:s:v")) != -1) { 81bde1ae23Sclaudio switch (ch) { 82bde1ae23Sclaudio case 'd': 83bde1ae23Sclaudio debug = 1; 84bde1ae23Sclaudio break; 85bde1ae23Sclaudio case 'n': 86bde1ae23Sclaudio vscsidev = optarg; 87bde1ae23Sclaudio break; 88bde1ae23Sclaudio case 's': 89bde1ae23Sclaudio ctrlsock = optarg; 90bde1ae23Sclaudio break; 91f91d3f97Ssthen case 'v': 92f91d3f97Ssthen verbose = 1; 93f91d3f97Ssthen break; 94bde1ae23Sclaudio default: 95bde1ae23Sclaudio usage(); 96bde1ae23Sclaudio /* NOTREACHED */ 97bde1ae23Sclaudio } 98bde1ae23Sclaudio } 99bde1ae23Sclaudio 100bde1ae23Sclaudio argc -= optind; 101bde1ae23Sclaudio argv += optind; 102bde1ae23Sclaudio 103bde1ae23Sclaudio if (argc > 0) 104bde1ae23Sclaudio usage(); 105bde1ae23Sclaudio 106bde1ae23Sclaudio /* check for root privileges */ 107bde1ae23Sclaudio if (geteuid()) 108bde1ae23Sclaudio errx(1, "need root privileges"); 109bde1ae23Sclaudio 110bde1ae23Sclaudio log_init(debug); 111f91d3f97Ssthen log_verbose(verbose); 112f91d3f97Ssthen 113442b4c84Sclaudio if (control_init(ctrlsock) == -1) 114442b4c84Sclaudio fatalx("control socket setup failed"); 115442b4c84Sclaudio 116bde1ae23Sclaudio if (!debug) 117bde1ae23Sclaudio daemon(1, 0); 118bde1ae23Sclaudio log_info("startup"); 119bde1ae23Sclaudio 12083d39657Sclaudio name[2] = getpid(); 12183d39657Sclaudio if (sysctl(name, 3, NULL, 0, &nobkill, sizeof(nobkill)) != 0) 12283d39657Sclaudio fatal("sysctl"); 12383d39657Sclaudio 124bde1ae23Sclaudio event_init(); 125bde1ae23Sclaudio vscsi_open(vscsidev); 126bde1ae23Sclaudio 127bde1ae23Sclaudio /* chroot and drop to iscsid user */ 128bde1ae23Sclaudio if ((pw = getpwnam(ISCSID_USER)) == NULL) 129bde1ae23Sclaudio errx(1, "unknown user %s", ISCSID_USER); 130bde1ae23Sclaudio 131bde1ae23Sclaudio if (chroot(pw->pw_dir) == -1) 132bde1ae23Sclaudio fatal("chroot"); 133bde1ae23Sclaudio if (chdir("/") == -1) 134bde1ae23Sclaudio fatal("chdir(\"/\")"); 135bde1ae23Sclaudio if (setgroups(1, &pw->pw_gid) || 136bde1ae23Sclaudio setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 137bde1ae23Sclaudio setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 138bde1ae23Sclaudio fatal("can't drop privileges"); 139bde1ae23Sclaudio 140bde1ae23Sclaudio /* setup signal handler */ 141bde1ae23Sclaudio signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 142bde1ae23Sclaudio signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 143bde1ae23Sclaudio signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); 144bde1ae23Sclaudio signal_add(&ev_sigint, NULL); 145bde1ae23Sclaudio signal_add(&ev_sigterm, NULL); 146bde1ae23Sclaudio signal_add(&ev_sighup, NULL); 147bde1ae23Sclaudio signal(SIGPIPE, SIG_IGN); 148bde1ae23Sclaudio 14977822a85Sclaudio control_event_init(); 150*7b833938Sclaudio initiator_init(); 151bde1ae23Sclaudio 152bde1ae23Sclaudio event_dispatch(); 153bde1ae23Sclaudio 1542e849a08Sclaudio /* do some cleanup on the way out */ 155bde1ae23Sclaudio control_cleanup(ctrlsock); 156*7b833938Sclaudio initiator_cleanup(); 157bde1ae23Sclaudio log_info("exiting."); 158bde1ae23Sclaudio return 0; 159bde1ae23Sclaudio } 160bde1ae23Sclaudio 1612e849a08Sclaudio void 1622e849a08Sclaudio shutdown_cb(int fd, short event, void *arg) 1632e849a08Sclaudio { 1642e849a08Sclaudio struct timeval tv; 1652e849a08Sclaudio 166*7b833938Sclaudio if (exit_rounds++ >= ISCSI_EXIT_WAIT || initiator_isdown()) 1672e849a08Sclaudio event_loopexit(NULL); 1682e849a08Sclaudio 1692e849a08Sclaudio timerclear(&tv); 1702e849a08Sclaudio tv.tv_sec = 1; 1712e849a08Sclaudio 1722e849a08Sclaudio if (evtimer_add(&exit_ev, &tv) == -1) 1732e849a08Sclaudio fatal("shutdown_cb"); 1742e849a08Sclaudio } 1752e849a08Sclaudio 176bde1ae23Sclaudio void 177bde1ae23Sclaudio main_sig_handler(int sig, short event, void *arg) 178bde1ae23Sclaudio { 17912bd4d7fSclaudio struct timeval tv; 18012bd4d7fSclaudio 181bde1ae23Sclaudio /* signal handler rules don't apply, libevent decouples for us */ 182bde1ae23Sclaudio switch (sig) { 183bde1ae23Sclaudio case SIGTERM: 184bde1ae23Sclaudio case SIGINT: 185bde1ae23Sclaudio case SIGHUP: 186*7b833938Sclaudio initiator_shutdown(); 18712bd4d7fSclaudio evtimer_set(&exit_ev, shutdown_cb, NULL); 18812bd4d7fSclaudio timerclear(&tv); 18912bd4d7fSclaudio if (evtimer_add(&exit_ev, &tv) == -1) 19012bd4d7fSclaudio fatal("main_sig_handler"); 191bde1ae23Sclaudio break; 192bde1ae23Sclaudio default: 193bde1ae23Sclaudio fatalx("unexpected signal"); 194bde1ae23Sclaudio /* NOTREACHED */ 195bde1ae23Sclaudio } 196bde1ae23Sclaudio } 197bde1ae23Sclaudio 198bde1ae23Sclaudio __dead void 199bde1ae23Sclaudio usage(void) 200bde1ae23Sclaudio { 201bde1ae23Sclaudio extern char *__progname; 202bde1ae23Sclaudio 203f91d3f97Ssthen fprintf(stderr, "usage: %s [-dv] [-n device] [-s socket]\n", 204bde1ae23Sclaudio __progname); 205bde1ae23Sclaudio exit(1); 206bde1ae23Sclaudio } 207bde1ae23Sclaudio 208bde1ae23Sclaudio void 209bde1ae23Sclaudio iscsid_ctrl_dispatch(void *ch, struct pdu *pdu) 210bde1ae23Sclaudio { 211bde1ae23Sclaudio struct ctrlmsghdr *cmh; 212bde1ae23Sclaudio struct initiator_config *ic; 213*7b833938Sclaudio struct session_head *sh; 214bde1ae23Sclaudio struct session_config *sc; 215bde1ae23Sclaudio struct session *s; 216ea99aae6Sclaudio struct session_poll p = { 0 }; 217ac43be81Sclaudio int *valp; 218bde1ae23Sclaudio 219bde1ae23Sclaudio cmh = pdu_getbuf(pdu, NULL, 0); 220bde1ae23Sclaudio if (cmh == NULL) 221bde1ae23Sclaudio goto done; 222bde1ae23Sclaudio 223bde1ae23Sclaudio switch (cmh->type) { 224bde1ae23Sclaudio case CTRL_INITIATOR_CONFIG: 225bde1ae23Sclaudio if (cmh->len[0] != sizeof(*ic)) { 226bde1ae23Sclaudio log_warnx("CTRL_INITIATOR_CONFIG bad size"); 227bde1ae23Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 228bde1ae23Sclaudio break; 229bde1ae23Sclaudio } 230bde1ae23Sclaudio ic = pdu_getbuf(pdu, NULL, 1); 231*7b833938Sclaudio initiator_set_config(ic); 232bde1ae23Sclaudio control_compose(ch, CTRL_SUCCESS, NULL, 0); 233bde1ae23Sclaudio break; 234bde1ae23Sclaudio case CTRL_SESSION_CONFIG: 235bde1ae23Sclaudio if (cmh->len[0] != sizeof(*sc)) { 236ac43be81Sclaudio log_warnx("CTRL_SESSION_CONFIG bad size"); 237bde1ae23Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 238bde1ae23Sclaudio break; 239bde1ae23Sclaudio } 240bde1ae23Sclaudio sc = pdu_getbuf(pdu, NULL, 1); 241bde1ae23Sclaudio if (cmh->len[1]) 242bde1ae23Sclaudio sc->TargetName = pdu_getbuf(pdu, NULL, 2); 243bde1ae23Sclaudio else if (sc->SessionType != SESSION_TYPE_DISCOVERY) { 244bde1ae23Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 245bde1ae23Sclaudio goto done; 246bde1ae23Sclaudio } else 247bde1ae23Sclaudio sc->TargetName = NULL; 248bde1ae23Sclaudio if (cmh->len[2]) 249bde1ae23Sclaudio sc->InitiatorName = pdu_getbuf(pdu, NULL, 3); 250bde1ae23Sclaudio else 251bde1ae23Sclaudio sc->InitiatorName = NULL; 252bde1ae23Sclaudio 253*7b833938Sclaudio s = initiator_find_session(sc->SessionName); 254bde1ae23Sclaudio if (s == NULL) { 255*7b833938Sclaudio s = initiator_new_session(sc->SessionType); 256bde1ae23Sclaudio if (s == NULL) { 257bde1ae23Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 258bde1ae23Sclaudio goto done; 259bde1ae23Sclaudio } 260bde1ae23Sclaudio } 261bde1ae23Sclaudio 262bde1ae23Sclaudio session_config(s, sc); 263cb408c6cSclaudio if (s->state == SESS_INIT) 2640908bd10Sclaudio session_fsm(&s->sev, SESS_EV_START, 0); 265bde1ae23Sclaudio 266bde1ae23Sclaudio control_compose(ch, CTRL_SUCCESS, NULL, 0); 267bde1ae23Sclaudio break; 268ac43be81Sclaudio case CTRL_LOG_VERBOSE: 269ac43be81Sclaudio if (cmh->len[0] != sizeof(int)) { 270ac43be81Sclaudio log_warnx("CTRL_LOG_VERBOSE bad size"); 271ac43be81Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 272ac43be81Sclaudio break; 273ac43be81Sclaudio } 274ac43be81Sclaudio valp = pdu_getbuf(pdu, NULL, 1); 275ac43be81Sclaudio log_verbose(*valp); 276ac43be81Sclaudio control_compose(ch, CTRL_SUCCESS, NULL, 0); 277ac43be81Sclaudio break; 278234d8810Sclaudio case CTRL_VSCSI_STATS: 279234d8810Sclaudio control_compose(ch, CTRL_VSCSI_STATS, vscsi_stats(), 280234d8810Sclaudio sizeof(struct vscsi_stats)); 281234d8810Sclaudio break; 2829683d5fdSclaudio case CTRL_SHOW_SUM: 283*7b833938Sclaudio ic = initiator_get_config(); 284*7b833938Sclaudio control_compose(ch, CTRL_INITIATOR_CONFIG, ic, sizeof(*ic)); 2859683d5fdSclaudio 286*7b833938Sclaudio sh = initiator_get_sessions(); 287*7b833938Sclaudio TAILQ_FOREACH(s, sh, entry) { 2885bee45bcSclaudio struct ctrldata cdv[3]; 2895bee45bcSclaudio bzero(cdv, sizeof(cdv)); 2905bee45bcSclaudio 2915bee45bcSclaudio cdv[0].buf = &s->config; 2925bee45bcSclaudio cdv[0].len = sizeof(s->config); 2935bee45bcSclaudio 2945bee45bcSclaudio if (s->config.TargetName) { 2955bee45bcSclaudio cdv[1].buf = s->config.TargetName; 2965bee45bcSclaudio cdv[1].len = 2975bee45bcSclaudio strlen(s->config.TargetName) + 1; 2985bee45bcSclaudio } 2995bee45bcSclaudio if (s->config.InitiatorName) { 3005bee45bcSclaudio cdv[2].buf = s->config.InitiatorName; 3015bee45bcSclaudio cdv[2].len = 3025bee45bcSclaudio strlen(s->config.InitiatorName) + 1; 3035bee45bcSclaudio } 3045bee45bcSclaudio 3055bee45bcSclaudio control_build(ch, CTRL_SESSION_CONFIG, 3065bee45bcSclaudio nitems(cdv), cdv); 3075bee45bcSclaudio } 3089683d5fdSclaudio 3099683d5fdSclaudio control_compose(ch, CTRL_SUCCESS, NULL, 0); 3109683d5fdSclaudio break; 311ea99aae6Sclaudio case CTRL_SESS_POLL: 312*7b833938Sclaudio sh = initiator_get_sessions(); 313*7b833938Sclaudio TAILQ_FOREACH(s, sh, entry) 314ea99aae6Sclaudio poll_session(&p, s); 315ea99aae6Sclaudio poll_finalize(&p); 316ea99aae6Sclaudio control_compose(ch, CTRL_SESS_POLL, &p, sizeof(p)); 317ea99aae6Sclaudio break; 318bde1ae23Sclaudio default: 319bde1ae23Sclaudio log_warnx("unknown control message type %d", cmh->type); 320bde1ae23Sclaudio control_compose(ch, CTRL_FAILURE, NULL, 0); 321bde1ae23Sclaudio break; 322bde1ae23Sclaudio } 323bde1ae23Sclaudio 324bde1ae23Sclaudio done: 325bde1ae23Sclaudio pdu_free(pdu); 326bde1ae23Sclaudio } 32712bd4d7fSclaudio 3284125a3c4Sclaudio #define MERGE_MIN(r, a, b, v) \ 3295640efa3Stedu r->v = (a->v < b->v ? a->v : b->v) 3304125a3c4Sclaudio #define MERGE_MAX(r, a, b, v) \ 3315640efa3Stedu r->v = (a->v > b->v ? a->v : b->v) 3324125a3c4Sclaudio #define MERGE_OR(r, a, b, v) \ 3335640efa3Stedu r->v = (a->v || b->v) 3344125a3c4Sclaudio #define MERGE_AND(r, a, b, v) \ 3355640efa3Stedu r->v = (a->v && b->v) 3364125a3c4Sclaudio 3374125a3c4Sclaudio void 3384125a3c4Sclaudio iscsi_merge_sess_params(struct session_params *res, 3394125a3c4Sclaudio struct session_params *mine, struct session_params *his) 3404125a3c4Sclaudio { 341aeec9c16Sclaudio memset(res, 0, sizeof(*res)); 342aeec9c16Sclaudio 3434125a3c4Sclaudio MERGE_MIN(res, mine, his, MaxBurstLength); 3444125a3c4Sclaudio MERGE_MIN(res, mine, his, FirstBurstLength); 3454125a3c4Sclaudio MERGE_MAX(res, mine, his, DefaultTime2Wait); 3464125a3c4Sclaudio MERGE_MIN(res, mine, his, DefaultTime2Retain); 3474125a3c4Sclaudio MERGE_MIN(res, mine, his, MaxOutstandingR2T); 3484125a3c4Sclaudio res->TargetPortalGroupTag = his->TargetPortalGroupTag; 3494125a3c4Sclaudio MERGE_MIN(res, mine, his, MaxConnections); 3504125a3c4Sclaudio MERGE_OR(res, mine, his, InitialR2T); 3514125a3c4Sclaudio MERGE_AND(res, mine, his, ImmediateData); 3524125a3c4Sclaudio MERGE_OR(res, mine, his, DataPDUInOrder); 3534125a3c4Sclaudio MERGE_OR(res, mine, his, DataSequenceInOrder); 3544125a3c4Sclaudio MERGE_MIN(res, mine, his, ErrorRecoveryLevel); 3554125a3c4Sclaudio 3564125a3c4Sclaudio } 3574125a3c4Sclaudio 3584125a3c4Sclaudio void 3594125a3c4Sclaudio iscsi_merge_conn_params(struct connection_params *res, 3604125a3c4Sclaudio struct connection_params *mine, struct connection_params *his) 3614125a3c4Sclaudio { 362aeec9c16Sclaudio int mask; 363aeec9c16Sclaudio 364aeec9c16Sclaudio memset(res, 0, sizeof(*res)); 365aeec9c16Sclaudio 3664125a3c4Sclaudio res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; 367aeec9c16Sclaudio 368aeec9c16Sclaudio /* for digest select first bit that is set in both his and mine */ 369aeec9c16Sclaudio mask = mine->HeaderDigest & his->HeaderDigest; 370aeec9c16Sclaudio mask = ffs(mask) - 1; 371aeec9c16Sclaudio if (mask == -1) 372aeec9c16Sclaudio res->HeaderDigest = 0; 373aeec9c16Sclaudio else 374aeec9c16Sclaudio res->HeaderDigest = 1 << mask; 375aeec9c16Sclaudio 376aeec9c16Sclaudio mask = mine->DataDigest & his->DataDigest; 377aeec9c16Sclaudio mask = ffs(mask) - 1; 378aeec9c16Sclaudio if (mask == -1) 379aeec9c16Sclaudio res->DataDigest = 0; 380aeec9c16Sclaudio else 381aeec9c16Sclaudio res->DataDigest = 1 << mask; 3824125a3c4Sclaudio } 3834125a3c4Sclaudio 3844125a3c4Sclaudio #undef MERGE_MIN 3854125a3c4Sclaudio #undef MERGE_MAX 3864125a3c4Sclaudio #undef MERGE_OR 3874125a3c4Sclaudio #undef MERGE_AND 388