kernel.c revision 272911
1/*-
2 * Copyright (c) 2003, 2004 Silicon Graphics International Corp.
3 * Copyright (c) 1997-2007 Kenneth D. Merry
4 * Copyright (c) 2012 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * Portions of this software were developed by Edward Tomasz Napierala
8 * under sponsorship from the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions, and the following disclaimer,
15 *    without modification.
16 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17 *    substantially similar to the "NO WARRANTY" disclaimer below
18 *    ("Disclaimer") and any redistribution must be conditioned upon
19 *    including a substantially similar Disclaimer requirement for further
20 *    binary redistribution.
21 *
22 * NO WARRANTY
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGES.
34 *
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/usr.sbin/ctld/kernel.c 272911 2014-10-10 19:41:09Z mav $");
39
40#include <sys/ioctl.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/param.h>
44#include <sys/linker.h>
45#include <sys/queue.h>
46#include <sys/callout.h>
47#include <sys/sbuf.h>
48#include <sys/capsicum.h>
49#include <assert.h>
50#include <bsdxml.h>
51#include <ctype.h>
52#include <errno.h>
53#include <fcntl.h>
54#include <stdint.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <strings.h>
59#include <cam/scsi/scsi_all.h>
60#include <cam/scsi/scsi_message.h>
61#include <cam/ctl/ctl.h>
62#include <cam/ctl/ctl_io.h>
63#include <cam/ctl/ctl_frontend_internal.h>
64#include <cam/ctl/ctl_backend.h>
65#include <cam/ctl/ctl_ioctl.h>
66#include <cam/ctl/ctl_backend_block.h>
67#include <cam/ctl/ctl_util.h>
68#include <cam/ctl/ctl_scsi_all.h>
69
70#include "ctld.h"
71
72#ifdef ICL_KERNEL_PROXY
73#include <netdb.h>
74#endif
75
76extern bool proxy_mode;
77
78static int	ctl_fd = 0;
79
80void
81kernel_init(void)
82{
83	int retval, saved_errno;
84
85	ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
86	if (ctl_fd < 0 && errno == ENOENT) {
87		saved_errno = errno;
88		retval = kldload("ctl");
89		if (retval != -1)
90			ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
91		else
92			errno = saved_errno;
93	}
94	if (ctl_fd < 0)
95		log_err(1, "failed to open %s", CTL_DEFAULT_DEV);
96}
97
98/*
99 * Name/value pair used for per-LUN attributes.
100 */
101struct cctl_lun_nv {
102	char *name;
103	char *value;
104	STAILQ_ENTRY(cctl_lun_nv) links;
105};
106
107/*
108 * Backend LUN information.
109 */
110struct cctl_lun {
111	uint64_t lun_id;
112	char *backend_type;
113	uint64_t size_blocks;
114	uint32_t blocksize;
115	char *serial_number;
116	char *device_id;
117	char *cfiscsi_target;
118	int cfiscsi_lun;
119	STAILQ_HEAD(,cctl_lun_nv) attr_list;
120	STAILQ_ENTRY(cctl_lun) links;
121};
122
123struct cctl_port {
124	uint32_t port_id;
125	int cfiscsi_status;
126	char *cfiscsi_target;
127	uint16_t cfiscsi_portal_group_tag;
128	STAILQ_HEAD(,cctl_lun_nv) attr_list;
129	STAILQ_ENTRY(cctl_port) links;
130};
131
132struct cctl_devlist_data {
133	int num_luns;
134	STAILQ_HEAD(,cctl_lun) lun_list;
135	struct cctl_lun *cur_lun;
136	int num_ports;
137	STAILQ_HEAD(,cctl_port) port_list;
138	struct cctl_port *cur_port;
139	int level;
140	struct sbuf *cur_sb[32];
141};
142
143static void
144cctl_start_element(void *user_data, const char *name, const char **attr)
145{
146	int i;
147	struct cctl_devlist_data *devlist;
148	struct cctl_lun *cur_lun;
149
150	devlist = (struct cctl_devlist_data *)user_data;
151	cur_lun = devlist->cur_lun;
152	devlist->level++;
153	if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) /
154	    sizeof(devlist->cur_sb[0])))
155		log_errx(1, "%s: too many nesting levels, %zd max", __func__,
156		     sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
157
158	devlist->cur_sb[devlist->level] = sbuf_new_auto();
159	if (devlist->cur_sb[devlist->level] == NULL)
160		log_err(1, "%s: unable to allocate sbuf", __func__);
161
162	if (strcmp(name, "lun") == 0) {
163		if (cur_lun != NULL)
164			log_errx(1, "%s: improper lun element nesting",
165			    __func__);
166
167		cur_lun = calloc(1, sizeof(*cur_lun));
168		if (cur_lun == NULL)
169			log_err(1, "%s: cannot allocate %zd bytes", __func__,
170			    sizeof(*cur_lun));
171
172		devlist->num_luns++;
173		devlist->cur_lun = cur_lun;
174
175		STAILQ_INIT(&cur_lun->attr_list);
176		STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links);
177
178		for (i = 0; attr[i] != NULL; i += 2) {
179			if (strcmp(attr[i], "id") == 0) {
180				cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
181			} else {
182				log_errx(1, "%s: invalid LUN attribute %s = %s",
183				     __func__, attr[i], attr[i+1]);
184			}
185		}
186	}
187}
188
189static void
190cctl_end_element(void *user_data, const char *name)
191{
192	struct cctl_devlist_data *devlist;
193	struct cctl_lun *cur_lun;
194	char *str;
195
196	devlist = (struct cctl_devlist_data *)user_data;
197	cur_lun = devlist->cur_lun;
198
199	if ((cur_lun == NULL)
200	 && (strcmp(name, "ctllunlist") != 0))
201		log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
202
203	if (devlist->cur_sb[devlist->level] == NULL)
204		log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
205		     devlist->level, name);
206
207	sbuf_finish(devlist->cur_sb[devlist->level]);
208	str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level]));
209
210	if (strlen(str) == 0) {
211		free(str);
212		str = NULL;
213	}
214
215	sbuf_delete(devlist->cur_sb[devlist->level]);
216	devlist->cur_sb[devlist->level] = NULL;
217	devlist->level--;
218
219	if (strcmp(name, "backend_type") == 0) {
220		cur_lun->backend_type = str;
221		str = NULL;
222	} else if (strcmp(name, "size") == 0) {
223		cur_lun->size_blocks = strtoull(str, NULL, 0);
224	} else if (strcmp(name, "blocksize") == 0) {
225		cur_lun->blocksize = strtoul(str, NULL, 0);
226	} else if (strcmp(name, "serial_number") == 0) {
227		cur_lun->serial_number = str;
228		str = NULL;
229	} else if (strcmp(name, "device_id") == 0) {
230		cur_lun->device_id = str;
231		str = NULL;
232	} else if (strcmp(name, "cfiscsi_target") == 0) {
233		cur_lun->cfiscsi_target = str;
234		str = NULL;
235	} else if (strcmp(name, "cfiscsi_lun") == 0) {
236		cur_lun->cfiscsi_lun = strtoul(str, NULL, 0);
237	} else if (strcmp(name, "lun") == 0) {
238		devlist->cur_lun = NULL;
239	} else if (strcmp(name, "ctllunlist") == 0) {
240
241	} else {
242		struct cctl_lun_nv *nv;
243
244		nv = calloc(1, sizeof(*nv));
245		if (nv == NULL)
246			log_err(1, "%s: can't allocate %zd bytes for nv pair",
247			    __func__, sizeof(*nv));
248
249		nv->name = checked_strdup(name);
250
251		nv->value = str;
252		str = NULL;
253		STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links);
254	}
255
256	free(str);
257}
258
259static void
260cctl_start_pelement(void *user_data, const char *name, const char **attr)
261{
262	int i;
263	struct cctl_devlist_data *devlist;
264	struct cctl_port *cur_port;
265
266	devlist = (struct cctl_devlist_data *)user_data;
267	cur_port = devlist->cur_port;
268	devlist->level++;
269	if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) /
270	    sizeof(devlist->cur_sb[0])))
271		log_errx(1, "%s: too many nesting levels, %zd max", __func__,
272		     sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
273
274	devlist->cur_sb[devlist->level] = sbuf_new_auto();
275	if (devlist->cur_sb[devlist->level] == NULL)
276		log_err(1, "%s: unable to allocate sbuf", __func__);
277
278	if (strcmp(name, "targ_port") == 0) {
279		if (cur_port != NULL)
280			log_errx(1, "%s: improper port element nesting (%s)",
281			    __func__, name);
282
283		cur_port = calloc(1, sizeof(*cur_port));
284		if (cur_port == NULL)
285			log_err(1, "%s: cannot allocate %zd bytes", __func__,
286			    sizeof(*cur_port));
287
288		devlist->num_ports++;
289		devlist->cur_port = cur_port;
290
291		STAILQ_INIT(&cur_port->attr_list);
292		STAILQ_INSERT_TAIL(&devlist->port_list, cur_port, links);
293
294		for (i = 0; attr[i] != NULL; i += 2) {
295			if (strcmp(attr[i], "id") == 0) {
296				cur_port->port_id = strtoul(attr[i+1], NULL, 0);
297			} else {
298				log_errx(1, "%s: invalid LUN attribute %s = %s",
299				     __func__, attr[i], attr[i+1]);
300			}
301		}
302	}
303}
304
305static void
306cctl_end_pelement(void *user_data, const char *name)
307{
308	struct cctl_devlist_data *devlist;
309	struct cctl_port *cur_port;
310	char *str;
311
312	devlist = (struct cctl_devlist_data *)user_data;
313	cur_port = devlist->cur_port;
314
315	if ((cur_port == NULL)
316	 && (strcmp(name, "ctlportlist") != 0))
317		log_errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name);
318
319	if (devlist->cur_sb[devlist->level] == NULL)
320		log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
321		     devlist->level, name);
322
323	sbuf_finish(devlist->cur_sb[devlist->level]);
324	str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level]));
325
326	if (strlen(str) == 0) {
327		free(str);
328		str = NULL;
329	}
330
331	sbuf_delete(devlist->cur_sb[devlist->level]);
332	devlist->cur_sb[devlist->level] = NULL;
333	devlist->level--;
334
335	if (strcmp(name, "cfiscsi_target") == 0) {
336		cur_port->cfiscsi_target = str;
337		str = NULL;
338	} else if (strcmp(name, "cfiscsi_status") == 0) {
339		cur_port->cfiscsi_status = strtoul(str, NULL, 0);
340	} else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) {
341		cur_port->cfiscsi_portal_group_tag = strtoul(str, NULL, 0);
342	} else if (strcmp(name, "targ_port") == 0) {
343		devlist->cur_port = NULL;
344	} else if (strcmp(name, "ctlportlist") == 0) {
345
346	} else {
347		struct cctl_lun_nv *nv;
348
349		nv = calloc(1, sizeof(*nv));
350		if (nv == NULL)
351			log_err(1, "%s: can't allocate %zd bytes for nv pair",
352			    __func__, sizeof(*nv));
353
354		nv->name = checked_strdup(name);
355
356		nv->value = str;
357		str = NULL;
358		STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links);
359	}
360
361	free(str);
362}
363
364static void
365cctl_char_handler(void *user_data, const XML_Char *str, int len)
366{
367	struct cctl_devlist_data *devlist;
368
369	devlist = (struct cctl_devlist_data *)user_data;
370
371	sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
372}
373
374struct conf *
375conf_new_from_kernel(void)
376{
377	struct conf *conf = NULL;
378	struct target *targ;
379	struct lun *cl;
380	struct lun_option *lo;
381	struct ctl_lun_list list;
382	struct cctl_devlist_data devlist;
383	struct cctl_lun *lun;
384	struct cctl_port *port;
385	XML_Parser parser;
386	char *str;
387	int len, retval;
388
389	bzero(&devlist, sizeof(devlist));
390	STAILQ_INIT(&devlist.lun_list);
391	STAILQ_INIT(&devlist.port_list);
392
393	log_debugx("obtaining previously configured CTL luns from the kernel");
394
395	str = NULL;
396	len = 4096;
397retry:
398	str = realloc(str, len);
399	if (str == NULL)
400		log_err(1, "realloc");
401
402	bzero(&list, sizeof(list));
403	list.alloc_len = len;
404	list.status = CTL_LUN_LIST_NONE;
405	list.lun_xml = str;
406
407	if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) {
408		log_warn("error issuing CTL_LUN_LIST ioctl");
409		free(str);
410		return (NULL);
411	}
412
413	if (list.status == CTL_LUN_LIST_ERROR) {
414		log_warnx("error returned from CTL_LUN_LIST ioctl: %s",
415		    list.error_str);
416		free(str);
417		return (NULL);
418	}
419
420	if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
421		len = len << 1;
422		goto retry;
423	}
424
425	parser = XML_ParserCreate(NULL);
426	if (parser == NULL) {
427		log_warnx("unable to create XML parser");
428		free(str);
429		return (NULL);
430	}
431
432	XML_SetUserData(parser, &devlist);
433	XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
434	XML_SetCharacterDataHandler(parser, cctl_char_handler);
435
436	retval = XML_Parse(parser, str, strlen(str), 1);
437	XML_ParserFree(parser);
438	free(str);
439	if (retval != 1) {
440		log_warnx("XML_Parse failed");
441		return (NULL);
442	}
443
444	str = NULL;
445	len = 4096;
446retry_port:
447	str = realloc(str, len);
448	if (str == NULL)
449		log_err(1, "realloc");
450
451	bzero(&list, sizeof(list));
452	list.alloc_len = len;
453	list.status = CTL_LUN_LIST_NONE;
454	list.lun_xml = str;
455
456	if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) {
457		log_warn("error issuing CTL_PORT_LIST ioctl");
458		free(str);
459		return (NULL);
460	}
461
462	if (list.status == CTL_PORT_LIST_ERROR) {
463		log_warnx("error returned from CTL_PORT_LIST ioctl: %s",
464		    list.error_str);
465		free(str);
466		return (NULL);
467	}
468
469	if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
470		len = len << 1;
471		goto retry_port;
472	}
473
474	parser = XML_ParserCreate(NULL);
475	if (parser == NULL) {
476		log_warnx("unable to create XML parser");
477		free(str);
478		return (NULL);
479	}
480
481	XML_SetUserData(parser, &devlist);
482	XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement);
483	XML_SetCharacterDataHandler(parser, cctl_char_handler);
484
485	retval = XML_Parse(parser, str, strlen(str), 1);
486	XML_ParserFree(parser);
487	free(str);
488	if (retval != 1) {
489		log_warnx("XML_Parse failed");
490		return (NULL);
491	}
492
493	conf = conf_new();
494
495	STAILQ_FOREACH(port, &devlist.port_list, links) {
496
497		if (port->cfiscsi_target == NULL) {
498			log_debugx("CTL port %ju wasn't managed by ctld; "
499			    "ignoring", (uintmax_t)port->port_id);
500			continue;
501		}
502		if (port->cfiscsi_status != 1) {
503			log_debugx("CTL port %ju is not active (%d); ignoring",
504			    (uintmax_t)port->port_id, port->cfiscsi_status);
505			continue;
506		}
507
508		targ = target_find(conf, port->cfiscsi_target);
509		if (targ == NULL) {
510#if 0
511			log_debugx("found new kernel target %s for CTL port %ld",
512			    port->cfiscsi_target, port->port_id);
513#endif
514			targ = target_new(conf, port->cfiscsi_target);
515			if (targ == NULL) {
516				log_warnx("target_new failed");
517				continue;
518			}
519		}
520	}
521
522	STAILQ_FOREACH(lun, &devlist.lun_list, links) {
523		struct cctl_lun_nv *nv;
524
525		if (lun->cfiscsi_target == NULL) {
526			log_debugx("CTL lun %ju wasn't managed by ctld; "
527			    "ignoring", (uintmax_t)lun->lun_id);
528			continue;
529		}
530
531		targ = target_find(conf, lun->cfiscsi_target);
532		if (targ == NULL) {
533#if 0
534			log_debugx("found new kernel target %s for CTL lun %ld",
535			    lun->cfiscsi_target, lun->lun_id);
536#endif
537			targ = target_new(conf, lun->cfiscsi_target);
538			if (targ == NULL) {
539				log_warnx("target_new failed");
540				continue;
541			}
542		}
543
544		cl = lun_find(targ, lun->cfiscsi_lun);
545		if (cl != NULL) {
546			log_warnx("found CTL lun %ju, backing lun %d, target "
547			    "%s, also backed by CTL lun %d; ignoring",
548			    (uintmax_t) lun->lun_id, cl->l_lun,
549			    cl->l_target->t_name, cl->l_ctl_lun);
550			continue;
551		}
552
553		log_debugx("found CTL lun %ju, backing lun %d, target %s",
554		    (uintmax_t)lun->lun_id, lun->cfiscsi_lun, lun->cfiscsi_target);
555
556		cl = lun_new(targ, lun->cfiscsi_lun);
557		if (cl == NULL) {
558			log_warnx("lun_new failed");
559			continue;
560		}
561		lun_set_backend(cl, lun->backend_type);
562		lun_set_blocksize(cl, lun->blocksize);
563		lun_set_device_id(cl, lun->device_id);
564		lun_set_serial(cl, lun->serial_number);
565		lun_set_size(cl, lun->size_blocks * cl->l_blocksize);
566		lun_set_ctl_lun(cl, lun->lun_id);
567
568		STAILQ_FOREACH(nv, &lun->attr_list, links) {
569			if (strcmp(nv->name, "file") == 0 ||
570			    strcmp(nv->name, "dev") == 0) {
571				lun_set_path(cl, nv->value);
572				continue;
573			}
574			lo = lun_option_new(cl, nv->name, nv->value);
575			if (lo == NULL)
576				log_warnx("unable to add CTL lun option %s "
577				    "for CTL lun %ju for lun %d, target %s",
578				    nv->name, (uintmax_t) lun->lun_id,
579				    cl->l_lun, cl->l_target->t_name);
580		}
581	}
582
583	return (conf);
584}
585
586static void
587str_arg(struct ctl_be_arg *arg, const char *name, const char *value)
588{
589
590	arg->namelen = strlen(name) + 1;
591	arg->name = __DECONST(char *, name);
592	arg->vallen = strlen(value) + 1;
593	arg->value = __DECONST(char *, value);
594	arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
595}
596
597int
598kernel_lun_add(struct lun *lun)
599{
600	struct lun_option *lo;
601	struct ctl_lun_req req;
602	char *tmp;
603	int error, i, num_options;
604
605	bzero(&req, sizeof(req));
606
607	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
608	req.reqtype = CTL_LUNREQ_CREATE;
609
610	req.reqdata.create.blocksize_bytes = lun->l_blocksize;
611
612	if (lun->l_size != 0)
613		req.reqdata.create.lun_size_bytes = lun->l_size;
614
615	req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
616	req.reqdata.create.device_type = T_DIRECT;
617
618	if (lun->l_serial != NULL) {
619		strncpy(req.reqdata.create.serial_num, lun->l_serial,
620			sizeof(req.reqdata.create.serial_num));
621		req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
622	}
623
624	if (lun->l_device_id != NULL) {
625		strncpy(req.reqdata.create.device_id, lun->l_device_id,
626			sizeof(req.reqdata.create.device_id));
627		req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
628	}
629
630	if (lun->l_path != NULL) {
631		lo = lun_option_find(lun, "file");
632		if (lo != NULL) {
633			lun_option_set(lo, lun->l_path);
634		} else {
635			lo = lun_option_new(lun, "file", lun->l_path);
636			assert(lo != NULL);
637		}
638	}
639
640	lo = lun_option_find(lun, "cfiscsi_target");
641	if (lo != NULL) {
642		lun_option_set(lo, lun->l_target->t_name);
643	} else {
644		lo = lun_option_new(lun, "cfiscsi_target",
645		    lun->l_target->t_name);
646		assert(lo != NULL);
647	}
648
649	asprintf(&tmp, "%d", lun->l_lun);
650	if (tmp == NULL)
651		log_errx(1, "asprintf");
652	lo = lun_option_find(lun, "cfiscsi_lun");
653	if (lo != NULL) {
654		lun_option_set(lo, tmp);
655		free(tmp);
656	} else {
657		lo = lun_option_new(lun, "cfiscsi_lun", tmp);
658		free(tmp);
659		assert(lo != NULL);
660	}
661
662	asprintf(&tmp, "%s,lun,%d", lun->l_target->t_name, lun->l_lun);
663	if (tmp == NULL)
664		log_errx(1, "asprintf");
665	lo = lun_option_find(lun, "scsiname");
666	if (lo != NULL) {
667		lun_option_set(lo, tmp);
668		free(tmp);
669	} else {
670		lo = lun_option_new(lun, "scsiname", tmp);
671		free(tmp);
672		assert(lo != NULL);
673	}
674
675	num_options = 0;
676	TAILQ_FOREACH(lo, &lun->l_options, lo_next)
677		num_options++;
678
679	req.num_be_args = num_options;
680	if (num_options > 0) {
681		req.be_args = malloc(num_options * sizeof(*req.be_args));
682		if (req.be_args == NULL) {
683			log_warn("error allocating %zd bytes",
684			    num_options * sizeof(*req.be_args));
685			return (1);
686		}
687
688		i = 0;
689		TAILQ_FOREACH(lo, &lun->l_options, lo_next) {
690			str_arg(&req.be_args[i], lo->lo_name, lo->lo_value);
691			i++;
692		}
693		assert(i == num_options);
694	}
695
696	error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
697	free(req.be_args);
698	if (error != 0) {
699		log_warn("error issuing CTL_LUN_REQ ioctl");
700		return (1);
701	}
702
703	switch (req.status) {
704	case CTL_LUN_ERROR:
705		log_warnx("LUN creation error: %s", req.error_str);
706		return (1);
707	case CTL_LUN_WARNING:
708		log_warnx("LUN creation warning: %s", req.error_str);
709		break;
710	case CTL_LUN_OK:
711		break;
712	default:
713		log_warnx("unknown LUN creation status: %d",
714		    req.status);
715		return (1);
716	}
717
718	lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id);
719	return (0);
720}
721
722int
723kernel_lun_resize(struct lun *lun)
724{
725	struct ctl_lun_req req;
726
727	bzero(&req, sizeof(req));
728
729	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
730	req.reqtype = CTL_LUNREQ_MODIFY;
731
732	req.reqdata.modify.lun_id = lun->l_ctl_lun;
733	req.reqdata.modify.lun_size_bytes = lun->l_size;
734
735	if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
736		log_warn("error issuing CTL_LUN_REQ ioctl");
737		return (1);
738	}
739
740	switch (req.status) {
741	case CTL_LUN_ERROR:
742		log_warnx("LUN modification error: %s", req.error_str);
743		return (1);
744	case CTL_LUN_WARNING:
745		log_warnx("LUN modification warning: %s", req.error_str);
746		break;
747	case CTL_LUN_OK:
748		break;
749	default:
750		log_warnx("unknown LUN modification status: %d",
751		    req.status);
752		return (1);
753	}
754
755	return (0);
756}
757
758int
759kernel_lun_remove(struct lun *lun)
760{
761	struct ctl_lun_req req;
762
763	bzero(&req, sizeof(req));
764
765	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
766	req.reqtype = CTL_LUNREQ_RM;
767
768	req.reqdata.rm.lun_id = lun->l_ctl_lun;
769
770	if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
771		log_warn("error issuing CTL_LUN_REQ ioctl");
772		return (1);
773	}
774
775	switch (req.status) {
776	case CTL_LUN_ERROR:
777		log_warnx("LUN removal error: %s", req.error_str);
778		return (1);
779	case CTL_LUN_WARNING:
780		log_warnx("LUN removal warning: %s", req.error_str);
781		break;
782	case CTL_LUN_OK:
783		break;
784	default:
785		log_warnx("unknown LUN removal status: %d", req.status);
786		return (1);
787	}
788
789	return (0);
790}
791
792void
793kernel_handoff(struct connection *conn)
794{
795	struct ctl_iscsi req;
796
797	bzero(&req, sizeof(req));
798
799	req.type = CTL_ISCSI_HANDOFF;
800	strlcpy(req.data.handoff.initiator_name,
801	    conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name));
802	strlcpy(req.data.handoff.initiator_addr,
803	    conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr));
804	if (conn->conn_initiator_alias != NULL) {
805		strlcpy(req.data.handoff.initiator_alias,
806		    conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias));
807	}
808	memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid,
809	    sizeof(req.data.handoff.initiator_isid));
810	strlcpy(req.data.handoff.target_name,
811	    conn->conn_target->t_name, sizeof(req.data.handoff.target_name));
812#ifdef ICL_KERNEL_PROXY
813	if (proxy_mode)
814		req.data.handoff.connection_id = conn->conn_socket;
815	else
816		req.data.handoff.socket = conn->conn_socket;
817#else
818	req.data.handoff.socket = conn->conn_socket;
819#endif
820	req.data.handoff.portal_group_tag =
821	    conn->conn_portal->p_portal_group->pg_tag;
822	if (conn->conn_header_digest == CONN_DIGEST_CRC32C)
823		req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
824	if (conn->conn_data_digest == CONN_DIGEST_CRC32C)
825		req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C;
826	req.data.handoff.cmdsn = conn->conn_cmdsn;
827	req.data.handoff.statsn = conn->conn_statsn;
828	req.data.handoff.max_recv_data_segment_length =
829	    conn->conn_max_data_segment_length;
830	req.data.handoff.max_burst_length = conn->conn_max_burst_length;
831	req.data.handoff.immediate_data = conn->conn_immediate_data;
832
833	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
834		log_err(1, "error issuing CTL_ISCSI ioctl; "
835		    "dropping connection");
836	}
837
838	if (req.status != CTL_ISCSI_OK) {
839		log_errx(1, "error returned from CTL iSCSI handoff request: "
840		    "%s; dropping connection", req.error_str);
841	}
842}
843
844int
845kernel_port_add(struct target *targ)
846{
847	struct ctl_port_entry entry;
848	struct ctl_req req;
849	char tagstr[16];
850	int error;
851	uint32_t port_id = -1;
852
853	bzero(&req, sizeof(req));
854	strlcpy(req.driver, "iscsi", sizeof(req.driver));
855	req.reqtype = CTL_REQ_CREATE;
856	req.num_args = 4;
857	req.args = malloc(req.num_args * sizeof(*req.args));
858	req.args[0].namelen = sizeof("port_id");
859	req.args[0].name = __DECONST(char *, "port_id");
860	req.args[0].vallen = sizeof(port_id);
861	req.args[0].value = &port_id;
862	req.args[0].flags = CTL_BEARG_WR;
863	str_arg(&req.args[1], "cfiscsi_target", targ->t_name);
864	snprintf(tagstr, sizeof(tagstr), "%d", targ->t_portal_group->pg_tag);
865	str_arg(&req.args[2], "cfiscsi_portal_group_tag", tagstr);
866	if (targ->t_alias)
867		str_arg(&req.args[3], "cfiscsi_target_alias", targ->t_alias);
868	else
869		req.num_args--;
870
871	error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
872	free(req.args);
873	if (error != 0) {
874		log_warn("error issuing CTL_PORT_REQ ioctl");
875		return (1);
876	}
877
878	if (req.status == CTL_LUN_ERROR) {
879		log_warnx("error returned from port creation request: %s",
880		    req.error_str);
881		return (1);
882	}
883
884	if (req.status != CTL_LUN_OK) {
885		log_warnx("unknown port creation request status %d",
886		    req.status);
887		return (1);
888	}
889
890	bzero(&entry, sizeof(entry));
891	entry.targ_port = port_id;
892
893	error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry);
894	if (error != 0) {
895		log_warn("CTL_ENABLE_PORT ioctl failed");
896		return (-1);
897	}
898
899	return (0);
900}
901
902int
903kernel_port_remove(struct target *targ)
904{
905	struct ctl_req req;
906	char tagstr[16];
907	int error;
908
909	bzero(&req, sizeof(req));
910	strlcpy(req.driver, "iscsi", sizeof(req.driver));
911	req.reqtype = CTL_REQ_REMOVE;
912	req.num_args = 2;
913	req.args = malloc(req.num_args * sizeof(*req.args));
914	str_arg(&req.args[0], "cfiscsi_target", targ->t_name);
915	if (targ->t_portal_group) {
916		snprintf(tagstr, sizeof(tagstr), "%d",
917		    targ->t_portal_group->pg_tag);
918		str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr);
919	} else
920		req.num_args--;
921
922	error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
923	free(req.args);
924	if (error != 0) {
925		log_warn("error issuing CTL_PORT_REQ ioctl");
926		return (1);
927	}
928
929	if (req.status == CTL_LUN_ERROR) {
930		log_warnx("error returned from port removal request: %s",
931		    req.error_str);
932		return (1);
933	}
934
935	if (req.status != CTL_LUN_OK) {
936		log_warnx("unknown port removal request status %d",
937		    req.status);
938		return (1);
939	}
940
941	return (0);
942}
943
944#ifdef ICL_KERNEL_PROXY
945void
946kernel_listen(struct addrinfo *ai, bool iser, int portal_id)
947{
948	struct ctl_iscsi req;
949
950	bzero(&req, sizeof(req));
951
952	req.type = CTL_ISCSI_LISTEN;
953	req.data.listen.iser = iser;
954	req.data.listen.domain = ai->ai_family;
955	req.data.listen.socktype = ai->ai_socktype;
956	req.data.listen.protocol = ai->ai_protocol;
957	req.data.listen.addr = ai->ai_addr;
958	req.data.listen.addrlen = ai->ai_addrlen;
959	req.data.listen.portal_id = portal_id;
960
961	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
962		log_err(1, "error issuing CTL_ISCSI ioctl");
963
964	if (req.status != CTL_ISCSI_OK) {
965		log_errx(1, "error returned from CTL iSCSI listen: %s",
966		    req.error_str);
967	}
968}
969
970void
971kernel_accept(int *connection_id, int *portal_id,
972    struct sockaddr *client_sa, socklen_t *client_salen)
973{
974	struct ctl_iscsi req;
975	struct sockaddr_storage ss;
976
977	bzero(&req, sizeof(req));
978
979	req.type = CTL_ISCSI_ACCEPT;
980	req.data.accept.initiator_addr = (struct sockaddr *)&ss;
981
982	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
983		log_err(1, "error issuing CTL_ISCSI ioctl");
984
985	if (req.status != CTL_ISCSI_OK) {
986		log_errx(1, "error returned from CTL iSCSI accept: %s",
987		    req.error_str);
988	}
989
990	*connection_id = req.data.accept.connection_id;
991	*portal_id = req.data.accept.portal_id;
992	*client_salen = req.data.accept.initiator_addrlen;
993	memcpy(client_sa, &ss, *client_salen);
994}
995
996void
997kernel_send(struct pdu *pdu)
998{
999	struct ctl_iscsi req;
1000
1001	bzero(&req, sizeof(req));
1002
1003	req.type = CTL_ISCSI_SEND;
1004	req.data.send.connection_id = pdu->pdu_connection->conn_socket;
1005	req.data.send.bhs = pdu->pdu_bhs;
1006	req.data.send.data_segment_len = pdu->pdu_data_len;
1007	req.data.send.data_segment = pdu->pdu_data;
1008
1009	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
1010		log_err(1, "error issuing CTL_ISCSI ioctl; "
1011		    "dropping connection");
1012	}
1013
1014	if (req.status != CTL_ISCSI_OK) {
1015		log_errx(1, "error returned from CTL iSCSI send: "
1016		    "%s; dropping connection", req.error_str);
1017	}
1018}
1019
1020void
1021kernel_receive(struct pdu *pdu)
1022{
1023	struct ctl_iscsi req;
1024
1025	pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH);
1026	if (pdu->pdu_data == NULL)
1027		log_err(1, "malloc");
1028
1029	bzero(&req, sizeof(req));
1030
1031	req.type = CTL_ISCSI_RECEIVE;
1032	req.data.receive.connection_id = pdu->pdu_connection->conn_socket;
1033	req.data.receive.bhs = pdu->pdu_bhs;
1034	req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH;
1035	req.data.receive.data_segment = pdu->pdu_data;
1036
1037	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
1038		log_err(1, "error issuing CTL_ISCSI ioctl; "
1039		    "dropping connection");
1040	}
1041
1042	if (req.status != CTL_ISCSI_OK) {
1043		log_errx(1, "error returned from CTL iSCSI receive: "
1044		    "%s; dropping connection", req.error_str);
1045	}
1046
1047}
1048
1049#endif /* ICL_KERNEL_PROXY */
1050
1051/*
1052 * XXX: I CANT INTO LATIN
1053 */
1054void
1055kernel_capsicate(void)
1056{
1057	int error;
1058	cap_rights_t rights;
1059	const unsigned long cmds[] = { CTL_ISCSI };
1060
1061	cap_rights_init(&rights, CAP_IOCTL);
1062	error = cap_rights_limit(ctl_fd, &rights);
1063	if (error != 0 && errno != ENOSYS)
1064		log_err(1, "cap_rights_limit");
1065
1066	error = cap_ioctls_limit(ctl_fd, cmds,
1067	    sizeof(cmds) / sizeof(cmds[0]));
1068	if (error != 0 && errno != ENOSYS)
1069		log_err(1, "cap_ioctls_limit");
1070
1071	error = cap_enter();
1072	if (error != 0 && errno != ENOSYS)
1073		log_err(1, "cap_enter");
1074
1075	if (cap_sandboxed())
1076		log_debugx("Capsicum capability mode enabled");
1077	else
1078		log_warnx("Capsicum capability mode not supported");
1079}
1080
1081