1 /* $OpenBSD: iscsid.c,v 1.25 2025/01/22 16:06:36 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 #include <sys/sysctl.h> 22 #include <sys/time.h> 23 #include <sys/uio.h> 24 25 #include <err.h> 26 #include <event.h> 27 #include <pwd.h> 28 #include <signal.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "iscsid.h" 35 #include "log.h" 36 37 void main_sig_handler(int, short, void *); 38 __dead void usage(void); 39 void shutdown_cb(int, short, void *); 40 41 struct event exit_ev; 42 int exit_rounds; 43 #define ISCSI_EXIT_WAIT 5 44 45 const struct session_params iscsi_sess_defaults = { 46 .MaxBurstLength = 262144, 47 .FirstBurstLength = 65536, 48 .DefaultTime2Wait = 2, 49 .DefaultTime2Retain = 20, 50 .MaxOutstandingR2T = 1, 51 .MaxConnections = 1, 52 .InitialR2T = 1, 53 .ImmediateData = 1, 54 .DataPDUInOrder = 1, 55 .DataSequenceInOrder = 1, 56 .ErrorRecoveryLevel = 0, 57 }; 58 59 const struct connection_params iscsi_conn_defaults = { 60 .MaxRecvDataSegmentLength = 8192, 61 .HeaderDigest = DIGEST_NONE, 62 .DataDigest = DIGEST_NONE, 63 }; 64 65 int 66 main(int argc, char *argv[]) 67 { 68 struct event ev_sigint, ev_sigterm, ev_sighup; 69 struct passwd *pw; 70 char *ctrlsock = ISCSID_CONTROL; 71 char *vscsidev = ISCSID_DEVICE; 72 int name[] = { CTL_KERN, KERN_PROC_NOBROADCASTKILL, 0 }; 73 int ch, debug = 0, verbose = 0, nobkill = 1; 74 75 log_procname = getprogname(); 76 77 log_init(1); /* log to stderr until daemonized */ 78 log_verbose(1); 79 80 while ((ch = getopt(argc, argv, "dn:s:v")) != -1) { 81 switch (ch) { 82 case 'd': 83 debug = 1; 84 break; 85 case 'n': 86 vscsidev = optarg; 87 break; 88 case 's': 89 ctrlsock = optarg; 90 break; 91 case 'v': 92 verbose = 1; 93 break; 94 default: 95 usage(); 96 /* NOTREACHED */ 97 } 98 } 99 100 argc -= optind; 101 argv += optind; 102 103 if (argc > 0) 104 usage(); 105 106 /* check for root privileges */ 107 if (geteuid()) 108 errx(1, "need root privileges"); 109 110 log_init(debug); 111 log_verbose(verbose); 112 113 if (control_init(ctrlsock) == -1) 114 fatalx("control socket setup failed"); 115 116 if (!debug) 117 daemon(1, 0); 118 log_info("startup"); 119 120 name[2] = getpid(); 121 if (sysctl(name, 3, NULL, 0, &nobkill, sizeof(nobkill)) != 0) 122 fatal("sysctl"); 123 124 event_init(); 125 vscsi_open(vscsidev); 126 127 /* chroot and drop to iscsid user */ 128 if ((pw = getpwnam(ISCSID_USER)) == NULL) 129 errx(1, "unknown user %s", ISCSID_USER); 130 131 if (chroot(pw->pw_dir) == -1) 132 fatal("chroot"); 133 if (chdir("/") == -1) 134 fatal("chdir(\"/\")"); 135 if (setgroups(1, &pw->pw_gid) || 136 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 137 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 138 fatal("can't drop privileges"); 139 140 /* setup signal handler */ 141 signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); 142 signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); 143 signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); 144 signal_add(&ev_sigint, NULL); 145 signal_add(&ev_sigterm, NULL); 146 signal_add(&ev_sighup, NULL); 147 signal(SIGPIPE, SIG_IGN); 148 149 control_event_init(); 150 initiator_init(); 151 152 event_dispatch(); 153 154 /* do some cleanup on the way out */ 155 control_cleanup(ctrlsock); 156 initiator_cleanup(); 157 log_info("exiting."); 158 return 0; 159 } 160 161 void 162 shutdown_cb(int fd, short event, void *arg) 163 { 164 struct timeval tv; 165 166 if (exit_rounds++ >= ISCSI_EXIT_WAIT || initiator_isdown()) 167 event_loopexit(NULL); 168 169 timerclear(&tv); 170 tv.tv_sec = 1; 171 172 if (evtimer_add(&exit_ev, &tv) == -1) 173 fatal("shutdown_cb"); 174 } 175 176 void 177 main_sig_handler(int sig, short event, void *arg) 178 { 179 struct timeval tv; 180 181 /* signal handler rules don't apply, libevent decouples for us */ 182 switch (sig) { 183 case SIGTERM: 184 case SIGINT: 185 case SIGHUP: 186 initiator_shutdown(); 187 evtimer_set(&exit_ev, shutdown_cb, NULL); 188 timerclear(&tv); 189 if (evtimer_add(&exit_ev, &tv) == -1) 190 fatal("main_sig_handler"); 191 break; 192 default: 193 fatalx("unexpected signal"); 194 /* NOTREACHED */ 195 } 196 } 197 198 __dead void 199 usage(void) 200 { 201 extern char *__progname; 202 203 fprintf(stderr, "usage: %s [-dv] [-n device] [-s socket]\n", 204 __progname); 205 exit(1); 206 } 207 208 void 209 iscsid_ctrl_dispatch(void *ch, struct pdu *pdu) 210 { 211 struct ctrlmsghdr *cmh; 212 struct initiator_config *ic; 213 struct session_head *sh; 214 struct session_config *sc; 215 struct session *s; 216 struct session_poll p = { 0 }; 217 int *valp; 218 219 cmh = pdu_getbuf(pdu, NULL, 0); 220 if (cmh == NULL) 221 goto done; 222 223 switch (cmh->type) { 224 case CTRL_INITIATOR_CONFIG: 225 if (cmh->len[0] != sizeof(*ic)) { 226 log_warnx("CTRL_INITIATOR_CONFIG bad size"); 227 control_compose(ch, CTRL_FAILURE, NULL, 0); 228 break; 229 } 230 ic = pdu_getbuf(pdu, NULL, 1); 231 initiator_set_config(ic); 232 control_compose(ch, CTRL_SUCCESS, NULL, 0); 233 break; 234 case CTRL_SESSION_CONFIG: 235 if (cmh->len[0] != sizeof(*sc)) { 236 log_warnx("CTRL_SESSION_CONFIG bad size"); 237 control_compose(ch, CTRL_FAILURE, NULL, 0); 238 break; 239 } 240 sc = pdu_getbuf(pdu, NULL, 1); 241 if (cmh->len[1]) 242 sc->TargetName = pdu_getbuf(pdu, NULL, 2); 243 else if (sc->SessionType != SESSION_TYPE_DISCOVERY) { 244 control_compose(ch, CTRL_FAILURE, NULL, 0); 245 goto done; 246 } else 247 sc->TargetName = NULL; 248 if (cmh->len[2]) 249 sc->InitiatorName = pdu_getbuf(pdu, NULL, 3); 250 else 251 sc->InitiatorName = NULL; 252 253 s = initiator_find_session(sc->SessionName); 254 if (s == NULL) { 255 s = initiator_new_session(sc->SessionType); 256 if (s == NULL) { 257 control_compose(ch, CTRL_FAILURE, NULL, 0); 258 goto done; 259 } 260 } 261 262 session_config(s, sc); 263 if (s->state == SESS_INIT) 264 session_fsm(&s->sev, SESS_EV_START, 0); 265 266 control_compose(ch, CTRL_SUCCESS, NULL, 0); 267 break; 268 case CTRL_LOG_VERBOSE: 269 if (cmh->len[0] != sizeof(int)) { 270 log_warnx("CTRL_LOG_VERBOSE bad size"); 271 control_compose(ch, CTRL_FAILURE, NULL, 0); 272 break; 273 } 274 valp = pdu_getbuf(pdu, NULL, 1); 275 log_verbose(*valp); 276 control_compose(ch, CTRL_SUCCESS, NULL, 0); 277 break; 278 case CTRL_VSCSI_STATS: 279 control_compose(ch, CTRL_VSCSI_STATS, vscsi_stats(), 280 sizeof(struct vscsi_stats)); 281 break; 282 case CTRL_SHOW_SUM: 283 ic = initiator_get_config(); 284 control_compose(ch, CTRL_INITIATOR_CONFIG, ic, sizeof(*ic)); 285 286 sh = initiator_get_sessions(); 287 TAILQ_FOREACH(s, sh, entry) { 288 struct ctrldata cdv[3]; 289 bzero(cdv, sizeof(cdv)); 290 291 cdv[0].buf = &s->config; 292 cdv[0].len = sizeof(s->config); 293 294 if (s->config.TargetName) { 295 cdv[1].buf = s->config.TargetName; 296 cdv[1].len = 297 strlen(s->config.TargetName) + 1; 298 } 299 if (s->config.InitiatorName) { 300 cdv[2].buf = s->config.InitiatorName; 301 cdv[2].len = 302 strlen(s->config.InitiatorName) + 1; 303 } 304 305 control_build(ch, CTRL_SESSION_CONFIG, 306 nitems(cdv), cdv); 307 } 308 309 control_compose(ch, CTRL_SUCCESS, NULL, 0); 310 break; 311 case CTRL_SESS_POLL: 312 sh = initiator_get_sessions(); 313 TAILQ_FOREACH(s, sh, entry) 314 poll_session(&p, s); 315 poll_finalize(&p); 316 control_compose(ch, CTRL_SESS_POLL, &p, sizeof(p)); 317 break; 318 default: 319 log_warnx("unknown control message type %d", cmh->type); 320 control_compose(ch, CTRL_FAILURE, NULL, 0); 321 break; 322 } 323 324 done: 325 pdu_free(pdu); 326 } 327 328 #define MERGE_MIN(r, a, b, v) \ 329 r->v = (a->v < b->v ? a->v : b->v) 330 #define MERGE_MAX(r, a, b, v) \ 331 r->v = (a->v > b->v ? a->v : b->v) 332 #define MERGE_OR(r, a, b, v) \ 333 r->v = (a->v || b->v) 334 #define MERGE_AND(r, a, b, v) \ 335 r->v = (a->v && b->v) 336 337 void 338 iscsi_merge_sess_params(struct session_params *res, 339 struct session_params *mine, struct session_params *his) 340 { 341 memset(res, 0, sizeof(*res)); 342 343 MERGE_MIN(res, mine, his, MaxBurstLength); 344 MERGE_MIN(res, mine, his, FirstBurstLength); 345 MERGE_MAX(res, mine, his, DefaultTime2Wait); 346 MERGE_MIN(res, mine, his, DefaultTime2Retain); 347 MERGE_MIN(res, mine, his, MaxOutstandingR2T); 348 res->TargetPortalGroupTag = his->TargetPortalGroupTag; 349 MERGE_MIN(res, mine, his, MaxConnections); 350 MERGE_OR(res, mine, his, InitialR2T); 351 MERGE_AND(res, mine, his, ImmediateData); 352 MERGE_OR(res, mine, his, DataPDUInOrder); 353 MERGE_OR(res, mine, his, DataSequenceInOrder); 354 MERGE_MIN(res, mine, his, ErrorRecoveryLevel); 355 356 } 357 358 void 359 iscsi_merge_conn_params(struct connection_params *res, 360 struct connection_params *mine, struct connection_params *his) 361 { 362 int mask; 363 364 memset(res, 0, sizeof(*res)); 365 366 res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; 367 368 /* for digest select first bit that is set in both his and mine */ 369 mask = mine->HeaderDigest & his->HeaderDigest; 370 mask = ffs(mask) - 1; 371 if (mask == -1) 372 res->HeaderDigest = 0; 373 else 374 res->HeaderDigest = 1 << mask; 375 376 mask = mine->DataDigest & his->DataDigest; 377 mask = ffs(mask) - 1; 378 if (mask == -1) 379 res->DataDigest = 0; 380 else 381 res->DataDigest = 1 << mask; 382 } 383 384 #undef MERGE_MIN 385 #undef MERGE_MAX 386 #undef MERGE_OR 387 #undef MERGE_AND 388