xref: /openbsd-src/usr.sbin/iscsid/iscsid.c (revision 7b8339380a5a5ef7c0e48ec10c485217e37b358f)
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