1255570Strasz/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
4255570Strasz * Copyright (c) 2003, 2004 Silicon Graphics International Corp.
5255570Strasz * Copyright (c) 1997-2007 Kenneth D. Merry
6255570Strasz * Copyright (c) 2012 The FreeBSD Foundation
7255570Strasz * All rights reserved.
8255570Strasz *
9255570Strasz * Portions of this software were developed by Edward Tomasz Napierala
10255570Strasz * under sponsorship from the FreeBSD Foundation.
11255570Strasz *
12255570Strasz * Redistribution and use in source and binary forms, with or without
13255570Strasz * modification, are permitted provided that the following conditions
14255570Strasz * are met:
15255570Strasz * 1. Redistributions of source code must retain the above copyright
16255570Strasz *    notice, this list of conditions, and the following disclaimer,
17255570Strasz *    without modification.
18255570Strasz * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19255570Strasz *    substantially similar to the "NO WARRANTY" disclaimer below
20255570Strasz *    ("Disclaimer") and any redistribution must be conditioned upon
21255570Strasz *    including a substantially similar Disclaimer requirement for further
22255570Strasz *    binary redistribution.
23255570Strasz *
24255570Strasz * NO WARRANTY
25255570Strasz * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26255570Strasz * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27255570Strasz * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
28255570Strasz * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29255570Strasz * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33255570Strasz * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
34255570Strasz * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35255570Strasz * POSSIBILITY OF SUCH DAMAGES.
36255570Strasz *
37255570Strasz */
38255570Strasz
39270279Strasz#include <sys/cdefs.h>
40270279Strasz__FBSDID("$FreeBSD: stable/11/usr.sbin/ctld/kernel.c 330449 2018-03-05 07:26:05Z eadler $");
41270279Strasz
42316252Sngie#include <sys/param.h>
43316252Sngie#include <sys/capsicum.h>
44316252Sngie#include <sys/callout.h>
45255570Strasz#include <sys/ioctl.h>
46255570Strasz#include <sys/linker.h>
47319152Sngie#include <sys/module.h>
48255570Strasz#include <sys/queue.h>
49255570Strasz#include <sys/sbuf.h>
50316252Sngie#include <sys/stat.h>
51255570Strasz#include <assert.h>
52255570Strasz#include <bsdxml.h>
53255570Strasz#include <ctype.h>
54255570Strasz#include <errno.h>
55255570Strasz#include <fcntl.h>
56255570Strasz#include <stdint.h>
57255570Strasz#include <stdio.h>
58255570Strasz#include <stdlib.h>
59255570Strasz#include <string.h>
60255570Strasz#include <strings.h>
61255570Strasz#include <cam/scsi/scsi_all.h>
62255570Strasz#include <cam/scsi/scsi_message.h>
63255570Strasz#include <cam/ctl/ctl.h>
64255570Strasz#include <cam/ctl/ctl_io.h>
65255570Strasz#include <cam/ctl/ctl_backend.h>
66255570Strasz#include <cam/ctl/ctl_ioctl.h>
67255570Strasz#include <cam/ctl/ctl_util.h>
68255570Strasz#include <cam/ctl/ctl_scsi_all.h>
69255570Strasz
70264524Strasz#include "ctld.h"
71264524Strasz
72255570Strasz#ifdef ICL_KERNEL_PROXY
73255570Strasz#include <netdb.h>
74255570Strasz#endif
75255570Strasz
76264524Straszextern bool proxy_mode;
77255570Strasz
78255570Straszstatic int	ctl_fd = 0;
79255570Strasz
80255570Straszvoid
81255570Straszkernel_init(void)
82255570Strasz{
83255570Strasz	int retval, saved_errno;
84255570Strasz
85255570Strasz	ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
86255665Strasz	if (ctl_fd < 0 && errno == ENOENT) {
87255570Strasz		saved_errno = errno;
88255570Strasz		retval = kldload("ctl");
89255570Strasz		if (retval != -1)
90255570Strasz			ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
91255570Strasz		else
92255570Strasz			errno = saved_errno;
93255570Strasz	}
94255570Strasz	if (ctl_fd < 0)
95255570Strasz		log_err(1, "failed to open %s", CTL_DEFAULT_DEV);
96319152Sngie#ifdef	WANT_ISCSI
97319152Sngie	else {
98319152Sngie		saved_errno = errno;
99319152Sngie		if (modfind("cfiscsi") == -1 && kldload("cfiscsi") == -1)
100319152Sngie			log_warn("couldn't load cfiscsi");
101319152Sngie		errno = saved_errno;
102319152Sngie	}
103319152Sngie#endif
104255570Strasz}
105255570Strasz
106255570Strasz/*
107255570Strasz * Name/value pair used for per-LUN attributes.
108255570Strasz */
109255570Straszstruct cctl_lun_nv {
110255570Strasz	char *name;
111255570Strasz	char *value;
112255570Strasz	STAILQ_ENTRY(cctl_lun_nv) links;
113255570Strasz};
114255570Strasz
115255570Strasz/*
116273464Strasz * Backend LUN information.
117255570Strasz */
118255570Straszstruct cctl_lun {
119255570Strasz	uint64_t lun_id;
120255570Strasz	char *backend_type;
121288310Smav	uint8_t device_type;
122255570Strasz	uint64_t size_blocks;
123255570Strasz	uint32_t blocksize;
124255570Strasz	char *serial_number;
125255570Strasz	char *device_id;
126278037Smav	char *ctld_name;
127255570Strasz	STAILQ_HEAD(,cctl_lun_nv) attr_list;
128255570Strasz	STAILQ_ENTRY(cctl_lun) links;
129255570Strasz};
130255570Strasz
131268291Smavstruct cctl_port {
132268291Smav	uint32_t port_id;
133288061Smav	char *port_frontend;
134278354Smav	char *port_name;
135284765Smav	int pp;
136284765Smav	int vp;
137278322Smav	int cfiscsi_state;
138268291Smav	char *cfiscsi_target;
139268291Smav	uint16_t cfiscsi_portal_group_tag;
140278322Smav	char *ctld_portal_group_name;
141268291Smav	STAILQ_HEAD(,cctl_lun_nv) attr_list;
142268291Smav	STAILQ_ENTRY(cctl_port) links;
143268291Smav};
144268291Smav
145255570Straszstruct cctl_devlist_data {
146255570Strasz	int num_luns;
147255570Strasz	STAILQ_HEAD(,cctl_lun) lun_list;
148255570Strasz	struct cctl_lun *cur_lun;
149268291Smav	int num_ports;
150268291Smav	STAILQ_HEAD(,cctl_port) port_list;
151268291Smav	struct cctl_port *cur_port;
152255570Strasz	int level;
153255570Strasz	struct sbuf *cur_sb[32];
154255570Strasz};
155255570Strasz
156255570Straszstatic void
157255570Straszcctl_start_element(void *user_data, const char *name, const char **attr)
158255570Strasz{
159255570Strasz	int i;
160255570Strasz	struct cctl_devlist_data *devlist;
161255570Strasz	struct cctl_lun *cur_lun;
162255570Strasz
163255570Strasz	devlist = (struct cctl_devlist_data *)user_data;
164255570Strasz	cur_lun = devlist->cur_lun;
165255570Strasz	devlist->level++;
166256189Strasz	if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) /
167255570Strasz	    sizeof(devlist->cur_sb[0])))
168255570Strasz		log_errx(1, "%s: too many nesting levels, %zd max", __func__,
169255570Strasz		     sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
170255570Strasz
171255570Strasz	devlist->cur_sb[devlist->level] = sbuf_new_auto();
172255570Strasz	if (devlist->cur_sb[devlist->level] == NULL)
173255570Strasz		log_err(1, "%s: unable to allocate sbuf", __func__);
174255570Strasz
175255570Strasz	if (strcmp(name, "lun") == 0) {
176255570Strasz		if (cur_lun != NULL)
177255570Strasz			log_errx(1, "%s: improper lun element nesting",
178255570Strasz			    __func__);
179255570Strasz
180255570Strasz		cur_lun = calloc(1, sizeof(*cur_lun));
181255570Strasz		if (cur_lun == NULL)
182255570Strasz			log_err(1, "%s: cannot allocate %zd bytes", __func__,
183255570Strasz			    sizeof(*cur_lun));
184255570Strasz
185255570Strasz		devlist->num_luns++;
186255570Strasz		devlist->cur_lun = cur_lun;
187255570Strasz
188255570Strasz		STAILQ_INIT(&cur_lun->attr_list);
189255570Strasz		STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links);
190255570Strasz
191255570Strasz		for (i = 0; attr[i] != NULL; i += 2) {
192255570Strasz			if (strcmp(attr[i], "id") == 0) {
193255570Strasz				cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
194255570Strasz			} else {
195255570Strasz				log_errx(1, "%s: invalid LUN attribute %s = %s",
196255570Strasz				     __func__, attr[i], attr[i+1]);
197255570Strasz			}
198255570Strasz		}
199255570Strasz	}
200255570Strasz}
201255570Strasz
202255570Straszstatic void
203255570Straszcctl_end_element(void *user_data, const char *name)
204255570Strasz{
205255570Strasz	struct cctl_devlist_data *devlist;
206255570Strasz	struct cctl_lun *cur_lun;
207255570Strasz	char *str;
208255570Strasz
209255570Strasz	devlist = (struct cctl_devlist_data *)user_data;
210255570Strasz	cur_lun = devlist->cur_lun;
211255570Strasz
212255570Strasz	if ((cur_lun == NULL)
213255570Strasz	 && (strcmp(name, "ctllunlist") != 0))
214255570Strasz		log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
215255570Strasz
216255570Strasz	if (devlist->cur_sb[devlist->level] == NULL)
217255570Strasz		log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
218255570Strasz		     devlist->level, name);
219255570Strasz
220255570Strasz	sbuf_finish(devlist->cur_sb[devlist->level]);
221255570Strasz	str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level]));
222255570Strasz
223255570Strasz	if (strlen(str) == 0) {
224255570Strasz		free(str);
225255570Strasz		str = NULL;
226255570Strasz	}
227255570Strasz
228255570Strasz	sbuf_delete(devlist->cur_sb[devlist->level]);
229255570Strasz	devlist->cur_sb[devlist->level] = NULL;
230255570Strasz	devlist->level--;
231255570Strasz
232255570Strasz	if (strcmp(name, "backend_type") == 0) {
233255570Strasz		cur_lun->backend_type = str;
234255570Strasz		str = NULL;
235288310Smav	} else if (strcmp(name, "lun_type") == 0) {
236288310Smav		cur_lun->device_type = strtoull(str, NULL, 0);
237255570Strasz	} else if (strcmp(name, "size") == 0) {
238255570Strasz		cur_lun->size_blocks = strtoull(str, NULL, 0);
239255570Strasz	} else if (strcmp(name, "blocksize") == 0) {
240255570Strasz		cur_lun->blocksize = strtoul(str, NULL, 0);
241255570Strasz	} else if (strcmp(name, "serial_number") == 0) {
242255570Strasz		cur_lun->serial_number = str;
243255570Strasz		str = NULL;
244255570Strasz	} else if (strcmp(name, "device_id") == 0) {
245255570Strasz		cur_lun->device_id = str;
246255570Strasz		str = NULL;
247278037Smav	} else if (strcmp(name, "ctld_name") == 0) {
248278037Smav		cur_lun->ctld_name = str;
249255570Strasz		str = NULL;
250255570Strasz	} else if (strcmp(name, "lun") == 0) {
251255570Strasz		devlist->cur_lun = NULL;
252255570Strasz	} else if (strcmp(name, "ctllunlist") == 0) {
253273464Strasz		/* Nothing. */
254255570Strasz	} else {
255255570Strasz		struct cctl_lun_nv *nv;
256255570Strasz
257255570Strasz		nv = calloc(1, sizeof(*nv));
258255570Strasz		if (nv == NULL)
259255570Strasz			log_err(1, "%s: can't allocate %zd bytes for nv pair",
260255570Strasz			    __func__, sizeof(*nv));
261255570Strasz
262255570Strasz		nv->name = checked_strdup(name);
263255570Strasz
264255570Strasz		nv->value = str;
265255570Strasz		str = NULL;
266255570Strasz		STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links);
267255570Strasz	}
268255570Strasz
269255570Strasz	free(str);
270255570Strasz}
271255570Strasz
272255570Straszstatic void
273268291Smavcctl_start_pelement(void *user_data, const char *name, const char **attr)
274268291Smav{
275268291Smav	int i;
276268291Smav	struct cctl_devlist_data *devlist;
277268291Smav	struct cctl_port *cur_port;
278268291Smav
279268291Smav	devlist = (struct cctl_devlist_data *)user_data;
280268291Smav	cur_port = devlist->cur_port;
281268291Smav	devlist->level++;
282268291Smav	if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) /
283268291Smav	    sizeof(devlist->cur_sb[0])))
284268291Smav		log_errx(1, "%s: too many nesting levels, %zd max", __func__,
285268291Smav		     sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
286268291Smav
287268291Smav	devlist->cur_sb[devlist->level] = sbuf_new_auto();
288268291Smav	if (devlist->cur_sb[devlist->level] == NULL)
289268291Smav		log_err(1, "%s: unable to allocate sbuf", __func__);
290268291Smav
291268291Smav	if (strcmp(name, "targ_port") == 0) {
292268291Smav		if (cur_port != NULL)
293268291Smav			log_errx(1, "%s: improper port element nesting (%s)",
294268291Smav			    __func__, name);
295268291Smav
296268291Smav		cur_port = calloc(1, sizeof(*cur_port));
297268291Smav		if (cur_port == NULL)
298268291Smav			log_err(1, "%s: cannot allocate %zd bytes", __func__,
299268291Smav			    sizeof(*cur_port));
300268291Smav
301268291Smav		devlist->num_ports++;
302268291Smav		devlist->cur_port = cur_port;
303268291Smav
304268291Smav		STAILQ_INIT(&cur_port->attr_list);
305268291Smav		STAILQ_INSERT_TAIL(&devlist->port_list, cur_port, links);
306268291Smav
307268291Smav		for (i = 0; attr[i] != NULL; i += 2) {
308268291Smav			if (strcmp(attr[i], "id") == 0) {
309268291Smav				cur_port->port_id = strtoul(attr[i+1], NULL, 0);
310268291Smav			} else {
311268291Smav				log_errx(1, "%s: invalid LUN attribute %s = %s",
312268291Smav				     __func__, attr[i], attr[i+1]);
313268291Smav			}
314268291Smav		}
315268291Smav	}
316268291Smav}
317268291Smav
318268291Smavstatic void
319268291Smavcctl_end_pelement(void *user_data, const char *name)
320268291Smav{
321268291Smav	struct cctl_devlist_data *devlist;
322268291Smav	struct cctl_port *cur_port;
323268291Smav	char *str;
324268291Smav
325268291Smav	devlist = (struct cctl_devlist_data *)user_data;
326268291Smav	cur_port = devlist->cur_port;
327268291Smav
328268291Smav	if ((cur_port == NULL)
329268291Smav	 && (strcmp(name, "ctlportlist") != 0))
330268291Smav		log_errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name);
331268291Smav
332268291Smav	if (devlist->cur_sb[devlist->level] == NULL)
333268291Smav		log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
334268291Smav		     devlist->level, name);
335268291Smav
336268291Smav	sbuf_finish(devlist->cur_sb[devlist->level]);
337268291Smav	str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level]));
338268291Smav
339268291Smav	if (strlen(str) == 0) {
340268291Smav		free(str);
341268291Smav		str = NULL;
342268291Smav	}
343268291Smav
344268291Smav	sbuf_delete(devlist->cur_sb[devlist->level]);
345268291Smav	devlist->cur_sb[devlist->level] = NULL;
346268291Smav	devlist->level--;
347268291Smav
348288061Smav	if (strcmp(name, "frontend_type") == 0) {
349288061Smav		cur_port->port_frontend = str;
350288061Smav		str = NULL;
351288061Smav	} else if (strcmp(name, "port_name") == 0) {
352278354Smav		cur_port->port_name = str;
353278354Smav		str = NULL;
354284765Smav	} else if (strcmp(name, "physical_port") == 0) {
355284765Smav		cur_port->pp = strtoul(str, NULL, 0);
356284765Smav	} else if (strcmp(name, "virtual_port") == 0) {
357284765Smav		cur_port->vp = strtoul(str, NULL, 0);
358278354Smav	} else if (strcmp(name, "cfiscsi_target") == 0) {
359268291Smav		cur_port->cfiscsi_target = str;
360268291Smav		str = NULL;
361278322Smav	} else if (strcmp(name, "cfiscsi_state") == 0) {
362278322Smav		cur_port->cfiscsi_state = strtoul(str, NULL, 0);
363268291Smav	} else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) {
364268291Smav		cur_port->cfiscsi_portal_group_tag = strtoul(str, NULL, 0);
365278322Smav	} else if (strcmp(name, "ctld_portal_group_name") == 0) {
366278322Smav		cur_port->ctld_portal_group_name = str;
367278322Smav		str = NULL;
368268291Smav	} else if (strcmp(name, "targ_port") == 0) {
369268291Smav		devlist->cur_port = NULL;
370268291Smav	} else if (strcmp(name, "ctlportlist") == 0) {
371273464Strasz		/* Nothing. */
372268291Smav	} else {
373268291Smav		struct cctl_lun_nv *nv;
374268291Smav
375268291Smav		nv = calloc(1, sizeof(*nv));
376268291Smav		if (nv == NULL)
377268291Smav			log_err(1, "%s: can't allocate %zd bytes for nv pair",
378268291Smav			    __func__, sizeof(*nv));
379268291Smav
380268291Smav		nv->name = checked_strdup(name);
381268291Smav
382268291Smav		nv->value = str;
383268291Smav		str = NULL;
384268291Smav		STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links);
385268291Smav	}
386268291Smav
387268291Smav	free(str);
388268291Smav}
389268291Smav
390268291Smavstatic void
391255570Straszcctl_char_handler(void *user_data, const XML_Char *str, int len)
392255570Strasz{
393255570Strasz	struct cctl_devlist_data *devlist;
394255570Strasz
395255570Strasz	devlist = (struct cctl_devlist_data *)user_data;
396255570Strasz
397255570Strasz	sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
398255570Strasz}
399255570Strasz
400255570Straszstruct conf *
401255570Straszconf_new_from_kernel(void)
402255570Strasz{
403255570Strasz	struct conf *conf = NULL;
404255570Strasz	struct target *targ;
405278322Smav	struct portal_group *pg;
406278354Smav	struct pport *pp;
407278322Smav	struct port *cp;
408255570Strasz	struct lun *cl;
409290615Smav	struct option *o;
410255570Strasz	struct ctl_lun_list list;
411255570Strasz	struct cctl_devlist_data devlist;
412255570Strasz	struct cctl_lun *lun;
413268291Smav	struct cctl_port *port;
414255570Strasz	XML_Parser parser;
415284765Smav	char *str, *name;
416268291Smav	int len, retval;
417255570Strasz
418255570Strasz	bzero(&devlist, sizeof(devlist));
419255570Strasz	STAILQ_INIT(&devlist.lun_list);
420268291Smav	STAILQ_INIT(&devlist.port_list);
421255570Strasz
422255570Strasz	log_debugx("obtaining previously configured CTL luns from the kernel");
423255570Strasz
424268291Smav	str = NULL;
425268291Smav	len = 4096;
426255570Straszretry:
427268291Smav	str = realloc(str, len);
428268291Smav	if (str == NULL)
429255570Strasz		log_err(1, "realloc");
430255570Strasz
431255570Strasz	bzero(&list, sizeof(list));
432268291Smav	list.alloc_len = len;
433255570Strasz	list.status = CTL_LUN_LIST_NONE;
434268291Smav	list.lun_xml = str;
435255570Strasz
436255570Strasz	if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) {
437255570Strasz		log_warn("error issuing CTL_LUN_LIST ioctl");
438268291Smav		free(str);
439255570Strasz		return (NULL);
440255570Strasz	}
441255570Strasz
442255570Strasz	if (list.status == CTL_LUN_LIST_ERROR) {
443255570Strasz		log_warnx("error returned from CTL_LUN_LIST ioctl: %s",
444255570Strasz		    list.error_str);
445268291Smav		free(str);
446255570Strasz		return (NULL);
447255570Strasz	}
448255570Strasz
449255570Strasz	if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
450268291Smav		len = len << 1;
451255570Strasz		goto retry;
452255570Strasz	}
453255570Strasz
454255570Strasz	parser = XML_ParserCreate(NULL);
455255570Strasz	if (parser == NULL) {
456255570Strasz		log_warnx("unable to create XML parser");
457268291Smav		free(str);
458255570Strasz		return (NULL);
459255570Strasz	}
460255570Strasz
461255570Strasz	XML_SetUserData(parser, &devlist);
462255570Strasz	XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
463255570Strasz	XML_SetCharacterDataHandler(parser, cctl_char_handler);
464255570Strasz
465268291Smav	retval = XML_Parse(parser, str, strlen(str), 1);
466255570Strasz	XML_ParserFree(parser);
467268291Smav	free(str);
468255570Strasz	if (retval != 1) {
469255570Strasz		log_warnx("XML_Parse failed");
470255570Strasz		return (NULL);
471255570Strasz	}
472255570Strasz
473268291Smav	str = NULL;
474268291Smav	len = 4096;
475268291Smavretry_port:
476268291Smav	str = realloc(str, len);
477268291Smav	if (str == NULL)
478268291Smav		log_err(1, "realloc");
479268291Smav
480268291Smav	bzero(&list, sizeof(list));
481268291Smav	list.alloc_len = len;
482268291Smav	list.status = CTL_LUN_LIST_NONE;
483268291Smav	list.lun_xml = str;
484268291Smav
485268291Smav	if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) {
486268291Smav		log_warn("error issuing CTL_PORT_LIST ioctl");
487268291Smav		free(str);
488268291Smav		return (NULL);
489268291Smav	}
490268291Smav
491288259Smav	if (list.status == CTL_LUN_LIST_ERROR) {
492268291Smav		log_warnx("error returned from CTL_PORT_LIST ioctl: %s",
493268291Smav		    list.error_str);
494268291Smav		free(str);
495268291Smav		return (NULL);
496268291Smav	}
497268291Smav
498268291Smav	if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
499268291Smav		len = len << 1;
500268291Smav		goto retry_port;
501268291Smav	}
502268291Smav
503268291Smav	parser = XML_ParserCreate(NULL);
504268291Smav	if (parser == NULL) {
505268291Smav		log_warnx("unable to create XML parser");
506268291Smav		free(str);
507268291Smav		return (NULL);
508268291Smav	}
509268291Smav
510268291Smav	XML_SetUserData(parser, &devlist);
511268291Smav	XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement);
512268291Smav	XML_SetCharacterDataHandler(parser, cctl_char_handler);
513268291Smav
514268291Smav	retval = XML_Parse(parser, str, strlen(str), 1);
515268291Smav	XML_ParserFree(parser);
516268291Smav	free(str);
517268291Smav	if (retval != 1) {
518268291Smav		log_warnx("XML_Parse failed");
519268291Smav		return (NULL);
520268291Smav	}
521268291Smav
522255570Strasz	conf = conf_new();
523255570Strasz
524284765Smav	name = NULL;
525268291Smav	STAILQ_FOREACH(port, &devlist.port_list, links) {
526288061Smav		if (strcmp(port->port_frontend, "ha") == 0)
527288061Smav			continue;
528290821Strasz		free(name);
529290824Strasz		if (port->pp == 0 && port->vp == 0) {
530284765Smav			name = checked_strdup(port->port_name);
531290824Strasz		} else if (port->vp == 0) {
532290824Strasz			retval = asprintf(&name, "%s/%d",
533290824Strasz			    port->port_name, port->pp);
534290824Strasz			if (retval <= 0)
535290824Strasz				log_err(1, "asprintf");
536290824Strasz		} else {
537290824Strasz			retval = asprintf(&name, "%s/%d/%d",
538290824Strasz			    port->port_name, port->pp, port->vp);
539290824Strasz			if (retval <= 0)
540290824Strasz				log_err(1, "asprintf");
541290824Strasz		}
542268291Smav
543268291Smav		if (port->cfiscsi_target == NULL) {
544278354Smav			log_debugx("CTL port %u \"%s\" wasn't managed by ctld; ",
545284765Smav			    port->port_id, name);
546284765Smav			pp = pport_find(conf, name);
547278354Smav			if (pp == NULL) {
548278354Smav#if 0
549278354Smav				log_debugx("found new kernel port %u \"%s\"",
550284765Smav				    port->port_id, name);
551278354Smav#endif
552284765Smav				pp = pport_new(conf, name, port->port_id);
553278354Smav				if (pp == NULL) {
554278354Smav					log_warnx("pport_new failed");
555278354Smav					continue;
556278354Smav				}
557278354Smav			}
558268291Smav			continue;
559268291Smav		}
560278322Smav		if (port->cfiscsi_state != 1) {
561268328Smav			log_debugx("CTL port %ju is not active (%d); ignoring",
562278322Smav			    (uintmax_t)port->port_id, port->cfiscsi_state);
563268328Smav			continue;
564268328Smav		}
565268291Smav
566268291Smav		targ = target_find(conf, port->cfiscsi_target);
567268291Smav		if (targ == NULL) {
568268291Smav#if 0
569268291Smav			log_debugx("found new kernel target %s for CTL port %ld",
570268291Smav			    port->cfiscsi_target, port->port_id);
571268291Smav#endif
572268291Smav			targ = target_new(conf, port->cfiscsi_target);
573268291Smav			if (targ == NULL) {
574268291Smav				log_warnx("target_new failed");
575268291Smav				continue;
576268291Smav			}
577268291Smav		}
578278322Smav
579278322Smav		if (port->ctld_portal_group_name == NULL)
580278322Smav			continue;
581278322Smav		pg = portal_group_find(conf, port->ctld_portal_group_name);
582278322Smav		if (pg == NULL) {
583278322Smav#if 0
584278322Smav			log_debugx("found new kernel portal group %s for CTL port %ld",
585278322Smav			    port->ctld_portal_group_name, port->port_id);
586278322Smav#endif
587278322Smav			pg = portal_group_new(conf, port->ctld_portal_group_name);
588278322Smav			if (pg == NULL) {
589278322Smav				log_warnx("portal_group_new failed");
590278322Smav				continue;
591278322Smav			}
592278322Smav		}
593278322Smav		pg->pg_tag = port->cfiscsi_portal_group_tag;
594278322Smav		cp = port_new(conf, targ, pg);
595278322Smav		if (cp == NULL) {
596278322Smav			log_warnx("port_new failed");
597278322Smav			continue;
598278322Smav		}
599278322Smav		cp->p_ctl_port = port->port_id;
600268291Smav	}
601290821Strasz	free(name);
602268291Smav
603255570Strasz	STAILQ_FOREACH(lun, &devlist.lun_list, links) {
604255570Strasz		struct cctl_lun_nv *nv;
605255570Strasz
606278037Smav		if (lun->ctld_name == NULL) {
607255570Strasz			log_debugx("CTL lun %ju wasn't managed by ctld; "
608255570Strasz			    "ignoring", (uintmax_t)lun->lun_id);
609255570Strasz			continue;
610255570Strasz		}
611255570Strasz
612278037Smav		cl = lun_find(conf, lun->ctld_name);
613255570Strasz		if (cl != NULL) {
614278037Smav			log_warnx("found CTL lun %ju \"%s\", "
615278037Smav			    "also backed by CTL lun %d; ignoring",
616278037Smav			    (uintmax_t)lun->lun_id, lun->ctld_name,
617278037Smav			    cl->l_ctl_lun);
618255570Strasz			continue;
619255570Strasz		}
620255570Strasz
621278037Smav		log_debugx("found CTL lun %ju \"%s\"",
622278037Smav		    (uintmax_t)lun->lun_id, lun->ctld_name);
623255570Strasz
624278037Smav		cl = lun_new(conf, lun->ctld_name);
625255570Strasz		if (cl == NULL) {
626255570Strasz			log_warnx("lun_new failed");
627255570Strasz			continue;
628255570Strasz		}
629255570Strasz		lun_set_backend(cl, lun->backend_type);
630288310Smav		lun_set_device_type(cl, lun->device_type);
631255570Strasz		lun_set_blocksize(cl, lun->blocksize);
632255570Strasz		lun_set_device_id(cl, lun->device_id);
633255570Strasz		lun_set_serial(cl, lun->serial_number);
634255570Strasz		lun_set_size(cl, lun->size_blocks * cl->l_blocksize);
635255570Strasz		lun_set_ctl_lun(cl, lun->lun_id);
636255570Strasz
637255570Strasz		STAILQ_FOREACH(nv, &lun->attr_list, links) {
638255570Strasz			if (strcmp(nv->name, "file") == 0 ||
639255570Strasz			    strcmp(nv->name, "dev") == 0) {
640255570Strasz				lun_set_path(cl, nv->value);
641255570Strasz				continue;
642255570Strasz			}
643290615Smav			o = option_new(&cl->l_options, nv->name, nv->value);
644290615Smav			if (o == NULL)
645255570Strasz				log_warnx("unable to add CTL lun option %s "
646278037Smav				    "for CTL lun %ju \"%s\"",
647255570Strasz				    nv->name, (uintmax_t) lun->lun_id,
648278037Smav				    cl->l_name);
649255570Strasz		}
650255570Strasz	}
651255570Strasz
652255570Strasz	return (conf);
653255570Strasz}
654255570Strasz
655268291Smavstatic void
656268291Smavstr_arg(struct ctl_be_arg *arg, const char *name, const char *value)
657268291Smav{
658268291Smav
659268291Smav	arg->namelen = strlen(name) + 1;
660268291Smav	arg->name = __DECONST(char *, name);
661268291Smav	arg->vallen = strlen(value) + 1;
662268291Smav	arg->value = __DECONST(char *, value);
663268291Smav	arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
664268291Smav}
665268291Smav
666255570Straszint
667255570Straszkernel_lun_add(struct lun *lun)
668255570Strasz{
669290615Smav	struct option *o;
670255570Strasz	struct ctl_lun_req req;
671255570Strasz	int error, i, num_options;
672255570Strasz
673255570Strasz	bzero(&req, sizeof(req));
674255570Strasz
675255570Strasz	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
676255570Strasz	req.reqtype = CTL_LUNREQ_CREATE;
677255570Strasz
678255570Strasz	req.reqdata.create.blocksize_bytes = lun->l_blocksize;
679255570Strasz
680255570Strasz	if (lun->l_size != 0)
681255570Strasz		req.reqdata.create.lun_size_bytes = lun->l_size;
682255570Strasz
683287823Smav	if (lun->l_ctl_lun >= 0) {
684287823Smav		req.reqdata.create.req_lun_id = lun->l_ctl_lun;
685287823Smav		req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ;
686287823Smav	}
687287823Smav
688255570Strasz	req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
689288310Smav	req.reqdata.create.device_type = lun->l_device_type;
690255570Strasz
691255570Strasz	if (lun->l_serial != NULL) {
692267648Smav		strncpy(req.reqdata.create.serial_num, lun->l_serial,
693255570Strasz			sizeof(req.reqdata.create.serial_num));
694255570Strasz		req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
695255570Strasz	}
696255570Strasz
697255570Strasz	if (lun->l_device_id != NULL) {
698267648Smav		strncpy(req.reqdata.create.device_id, lun->l_device_id,
699255570Strasz			sizeof(req.reqdata.create.device_id));
700255570Strasz		req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
701255570Strasz	}
702255570Strasz
703255570Strasz	if (lun->l_path != NULL) {
704290615Smav		o = option_find(&lun->l_options, "file");
705290615Smav		if (o != NULL) {
706290615Smav			option_set(o, lun->l_path);
707255570Strasz		} else {
708290615Smav			o = option_new(&lun->l_options, "file", lun->l_path);
709290615Smav			assert(o != NULL);
710255570Strasz		}
711255570Strasz	}
712255570Strasz
713290615Smav	o = option_find(&lun->l_options, "ctld_name");
714290615Smav	if (o != NULL) {
715290615Smav		option_set(o, lun->l_name);
716255570Strasz	} else {
717290615Smav		o = option_new(&lun->l_options, "ctld_name", lun->l_name);
718290615Smav		assert(o != NULL);
719255570Strasz	}
720255570Strasz
721290615Smav	o = option_find(&lun->l_options, "scsiname");
722290615Smav	if (o == NULL && lun->l_scsiname != NULL) {
723290615Smav		o = option_new(&lun->l_options, "scsiname", lun->l_scsiname);
724290615Smav		assert(o != NULL);
725268293Smav	}
726268293Smav
727255570Strasz	num_options = 0;
728290615Smav	TAILQ_FOREACH(o, &lun->l_options, o_next)
729255570Strasz		num_options++;
730255570Strasz
731255570Strasz	req.num_be_args = num_options;
732255570Strasz	if (num_options > 0) {
733255570Strasz		req.be_args = malloc(num_options * sizeof(*req.be_args));
734255570Strasz		if (req.be_args == NULL) {
735255570Strasz			log_warn("error allocating %zd bytes",
736255570Strasz			    num_options * sizeof(*req.be_args));
737255570Strasz			return (1);
738255570Strasz		}
739255570Strasz
740255570Strasz		i = 0;
741290615Smav		TAILQ_FOREACH(o, &lun->l_options, o_next) {
742290615Smav			str_arg(&req.be_args[i], o->o_name, o->o_value);
743255570Strasz			i++;
744255570Strasz		}
745255570Strasz		assert(i == num_options);
746255570Strasz	}
747255570Strasz
748255570Strasz	error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
749255570Strasz	free(req.be_args);
750255570Strasz	if (error != 0) {
751255570Strasz		log_warn("error issuing CTL_LUN_REQ ioctl");
752255570Strasz		return (1);
753255570Strasz	}
754255570Strasz
755272911Smav	switch (req.status) {
756272911Smav	case CTL_LUN_ERROR:
757272911Smav		log_warnx("LUN creation error: %s", req.error_str);
758255570Strasz		return (1);
759272911Smav	case CTL_LUN_WARNING:
760272911Smav		log_warnx("LUN creation warning: %s", req.error_str);
761272911Smav		break;
762272911Smav	case CTL_LUN_OK:
763272911Smav		break;
764272911Smav	default:
765272911Smav		log_warnx("unknown LUN creation status: %d",
766255570Strasz		    req.status);
767255570Strasz		return (1);
768255570Strasz	}
769255570Strasz
770255570Strasz	lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id);
771255570Strasz	return (0);
772255570Strasz}
773255570Strasz
774255570Straszint
775287500Smavkernel_lun_modify(struct lun *lun)
776255570Strasz{
777290615Smav	struct option *o;
778255570Strasz	struct ctl_lun_req req;
779287500Smav	int error, i, num_options;
780255570Strasz
781255570Strasz	bzero(&req, sizeof(req));
782255570Strasz
783255570Strasz	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
784255570Strasz	req.reqtype = CTL_LUNREQ_MODIFY;
785255570Strasz
786255570Strasz	req.reqdata.modify.lun_id = lun->l_ctl_lun;
787255570Strasz	req.reqdata.modify.lun_size_bytes = lun->l_size;
788255570Strasz
789287500Smav	num_options = 0;
790290615Smav	TAILQ_FOREACH(o, &lun->l_options, o_next)
791287500Smav		num_options++;
792287500Smav
793287500Smav	req.num_be_args = num_options;
794287500Smav	if (num_options > 0) {
795287500Smav		req.be_args = malloc(num_options * sizeof(*req.be_args));
796287500Smav		if (req.be_args == NULL) {
797287500Smav			log_warn("error allocating %zd bytes",
798287500Smav			    num_options * sizeof(*req.be_args));
799287500Smav			return (1);
800287500Smav		}
801287500Smav
802287500Smav		i = 0;
803290615Smav		TAILQ_FOREACH(o, &lun->l_options, o_next) {
804290615Smav			str_arg(&req.be_args[i], o->o_name, o->o_value);
805287500Smav			i++;
806287500Smav		}
807287500Smav		assert(i == num_options);
808287500Smav	}
809287500Smav
810287500Smav	error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
811287500Smav	free(req.be_args);
812287500Smav	if (error != 0) {
813255570Strasz		log_warn("error issuing CTL_LUN_REQ ioctl");
814255570Strasz		return (1);
815255570Strasz	}
816255570Strasz
817272911Smav	switch (req.status) {
818272911Smav	case CTL_LUN_ERROR:
819272911Smav		log_warnx("LUN modification error: %s", req.error_str);
820255570Strasz		return (1);
821272911Smav	case CTL_LUN_WARNING:
822272911Smav		log_warnx("LUN modification warning: %s", req.error_str);
823272911Smav		break;
824272911Smav	case CTL_LUN_OK:
825272911Smav		break;
826272911Smav	default:
827272911Smav		log_warnx("unknown LUN modification status: %d",
828255570Strasz		    req.status);
829255570Strasz		return (1);
830255570Strasz	}
831255570Strasz
832255570Strasz	return (0);
833255570Strasz}
834255570Strasz
835255570Straszint
836255570Straszkernel_lun_remove(struct lun *lun)
837255570Strasz{
838255570Strasz	struct ctl_lun_req req;
839255570Strasz
840255570Strasz	bzero(&req, sizeof(req));
841255570Strasz
842255570Strasz	strlcpy(req.backend, lun->l_backend, sizeof(req.backend));
843255570Strasz	req.reqtype = CTL_LUNREQ_RM;
844255570Strasz
845255570Strasz	req.reqdata.rm.lun_id = lun->l_ctl_lun;
846255570Strasz
847255570Strasz	if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) {
848255570Strasz		log_warn("error issuing CTL_LUN_REQ ioctl");
849255570Strasz		return (1);
850255570Strasz	}
851255570Strasz
852272911Smav	switch (req.status) {
853272911Smav	case CTL_LUN_ERROR:
854272911Smav		log_warnx("LUN removal error: %s", req.error_str);
855255570Strasz		return (1);
856272911Smav	case CTL_LUN_WARNING:
857272911Smav		log_warnx("LUN removal warning: %s", req.error_str);
858272911Smav		break;
859272911Smav	case CTL_LUN_OK:
860272911Smav		break;
861272911Smav	default:
862272911Smav		log_warnx("unknown LUN removal status: %d", req.status);
863255570Strasz		return (1);
864255570Strasz	}
865255570Strasz
866255570Strasz	return (0);
867255570Strasz}
868255570Strasz
869255570Straszvoid
870255570Straszkernel_handoff(struct connection *conn)
871255570Strasz{
872255570Strasz	struct ctl_iscsi req;
873255570Strasz
874255570Strasz	bzero(&req, sizeof(req));
875255570Strasz
876255570Strasz	req.type = CTL_ISCSI_HANDOFF;
877255570Strasz	strlcpy(req.data.handoff.initiator_name,
878255570Strasz	    conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name));
879255570Strasz	strlcpy(req.data.handoff.initiator_addr,
880255570Strasz	    conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr));
881255570Strasz	if (conn->conn_initiator_alias != NULL) {
882255570Strasz		strlcpy(req.data.handoff.initiator_alias,
883255570Strasz		    conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias));
884255570Strasz	}
885268302Smav	memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid,
886268302Smav	    sizeof(req.data.handoff.initiator_isid));
887255570Strasz	strlcpy(req.data.handoff.target_name,
888261757Strasz	    conn->conn_target->t_name, sizeof(req.data.handoff.target_name));
889279392Strasz	if (conn->conn_portal->p_portal_group->pg_offload != NULL) {
890278331Strasz		strlcpy(req.data.handoff.offload,
891279392Strasz		    conn->conn_portal->p_portal_group->pg_offload,
892279392Strasz		    sizeof(req.data.handoff.offload));
893278331Strasz	}
894264524Strasz#ifdef ICL_KERNEL_PROXY
895264524Strasz	if (proxy_mode)
896264524Strasz		req.data.handoff.connection_id = conn->conn_socket;
897264524Strasz	else
898264524Strasz		req.data.handoff.socket = conn->conn_socket;
899264524Strasz#else
900255570Strasz	req.data.handoff.socket = conn->conn_socket;
901264524Strasz#endif
902255570Strasz	req.data.handoff.portal_group_tag =
903255570Strasz	    conn->conn_portal->p_portal_group->pg_tag;
904255570Strasz	if (conn->conn_header_digest == CONN_DIGEST_CRC32C)
905255570Strasz		req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C;
906255570Strasz	if (conn->conn_data_digest == CONN_DIGEST_CRC32C)
907255570Strasz		req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C;
908255570Strasz	req.data.handoff.cmdsn = conn->conn_cmdsn;
909255570Strasz	req.data.handoff.statsn = conn->conn_statsn;
910255570Strasz	req.data.handoff.max_recv_data_segment_length =
911255570Strasz	    conn->conn_max_data_segment_length;
912255570Strasz	req.data.handoff.max_burst_length = conn->conn_max_burst_length;
913301437Strasz	req.data.handoff.first_burst_length = conn->conn_first_burst_length;
914255570Strasz	req.data.handoff.immediate_data = conn->conn_immediate_data;
915255570Strasz
916264526Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
917255570Strasz		log_err(1, "error issuing CTL_ISCSI ioctl; "
918255570Strasz		    "dropping connection");
919264526Strasz	}
920255570Strasz
921264526Strasz	if (req.status != CTL_ISCSI_OK) {
922255570Strasz		log_errx(1, "error returned from CTL iSCSI handoff request: "
923255570Strasz		    "%s; dropping connection", req.error_str);
924264526Strasz	}
925255570Strasz}
926255570Strasz
927278331Straszvoid
928278331Straszkernel_limits(const char *offload, size_t *max_data_segment_length)
929278331Strasz{
930278331Strasz	struct ctl_iscsi req;
931278331Strasz
932278331Strasz	bzero(&req, sizeof(req));
933278331Strasz
934278331Strasz	req.type = CTL_ISCSI_LIMITS;
935278331Strasz	if (offload != NULL) {
936278331Strasz		strlcpy(req.data.limits.offload, offload,
937278331Strasz		    sizeof(req.data.limits.offload));
938278331Strasz	}
939278331Strasz
940278331Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
941278331Strasz		log_err(1, "error issuing CTL_ISCSI ioctl; "
942278331Strasz		    "dropping connection");
943278331Strasz	}
944278331Strasz
945278331Strasz	if (req.status != CTL_ISCSI_OK) {
946278331Strasz		log_errx(1, "error returned from CTL iSCSI limits request: "
947278331Strasz		    "%s; dropping connection", req.error_str);
948278331Strasz	}
949278331Strasz
950278331Strasz	*max_data_segment_length = req.data.limits.data_segment_limit;
951278331Strasz	if (offload != NULL) {
952278331Strasz		log_debugx("MaxRecvDataSegment kernel limit for offload "
953278331Strasz		    "\"%s\" is %zd", offload, *max_data_segment_length);
954278331Strasz	} else {
955278331Strasz		log_debugx("MaxRecvDataSegment kernel limit is %zd",
956278331Strasz		    *max_data_segment_length);
957278331Strasz	}
958278331Strasz}
959278331Strasz
960255570Straszint
961278322Smavkernel_port_add(struct port *port)
962255570Strasz{
963290615Smav	struct option *o;
964255570Strasz	struct ctl_port_entry entry;
965268291Smav	struct ctl_req req;
966278037Smav	struct ctl_lun_map lm;
967278322Smav	struct target *targ = port->p_target;
968278322Smav	struct portal_group *pg = port->p_portal_group;
969268291Smav	char tagstr[16];
970278322Smav	int error, i, n;
971255570Strasz
972278037Smav	/* Create iSCSI port. */
973278354Smav	if (port->p_portal_group) {
974278354Smav		bzero(&req, sizeof(req));
975278354Smav		strlcpy(req.driver, "iscsi", sizeof(req.driver));
976278354Smav		req.reqtype = CTL_REQ_CREATE;
977279276Smav		req.num_args = 5;
978290615Smav		TAILQ_FOREACH(o, &pg->pg_options, o_next)
979290615Smav			req.num_args++;
980278354Smav		req.args = malloc(req.num_args * sizeof(*req.args));
981279276Smav		if (req.args == NULL)
982279276Smav			log_err(1, "malloc");
983278354Smav		n = 0;
984278354Smav		req.args[n].namelen = sizeof("port_id");
985278354Smav		req.args[n].name = __DECONST(char *, "port_id");
986278354Smav		req.args[n].vallen = sizeof(port->p_ctl_port);
987278354Smav		req.args[n].value = &port->p_ctl_port;
988278354Smav		req.args[n++].flags = CTL_BEARG_WR;
989278354Smav		str_arg(&req.args[n++], "cfiscsi_target", targ->t_name);
990278354Smav		snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
991278354Smav		str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr);
992278354Smav		if (targ->t_alias)
993278354Smav			str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias);
994278354Smav		str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name);
995290615Smav		TAILQ_FOREACH(o, &pg->pg_options, o_next)
996290615Smav			str_arg(&req.args[n++], o->o_name, o->o_value);
997278354Smav		req.num_args = n;
998278354Smav		error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
999278354Smav		free(req.args);
1000278354Smav		if (error != 0) {
1001278354Smav			log_warn("error issuing CTL_PORT_REQ ioctl");
1002278354Smav			return (1);
1003278354Smav		}
1004278354Smav		if (req.status == CTL_LUN_ERROR) {
1005278354Smav			log_warnx("error returned from port creation request: %s",
1006278354Smav			    req.error_str);
1007278354Smav			return (1);
1008278354Smav		}
1009278354Smav		if (req.status != CTL_LUN_OK) {
1010278354Smav			log_warnx("unknown port creation request status %d",
1011278354Smav			    req.status);
1012278354Smav			return (1);
1013278354Smav		}
1014279590Smav	} else if (port->p_pport) {
1015278354Smav		port->p_ctl_port = port->p_pport->pp_ctl_port;
1016268291Smav
1017279590Smav		if (strncmp(targ->t_name, "naa.", 4) == 0 &&
1018279590Smav		    strlen(targ->t_name) == 20) {
1019279590Smav			bzero(&entry, sizeof(entry));
1020279590Smav			entry.port_type = CTL_PORT_NONE;
1021279590Smav			entry.targ_port = port->p_ctl_port;
1022279590Smav			entry.flags |= CTL_PORT_WWNN_VALID;
1023279590Smav			entry.wwnn = strtoull(targ->t_name + 4, NULL, 16);
1024279590Smav			if (ioctl(ctl_fd, CTL_SET_PORT_WWNS, &entry) == -1)
1025279590Smav				log_warn("CTL_SET_PORT_WWNS ioctl failed");
1026279590Smav		}
1027279590Smav	}
1028279590Smav
1029278037Smav	/* Explicitly enable mapping to block any access except allowed. */
1030278322Smav	lm.port = port->p_ctl_port;
1031278037Smav	lm.plun = UINT32_MAX;
1032278037Smav	lm.lun = 0;
1033278037Smav	error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
1034278037Smav	if (error != 0)
1035278037Smav		log_warn("CTL_LUN_MAP ioctl failed");
1036278037Smav
1037278037Smav	/* Map configured LUNs */
1038278037Smav	for (i = 0; i < MAX_LUNS; i++) {
1039278037Smav		if (targ->t_luns[i] == NULL)
1040278037Smav			continue;
1041278322Smav		lm.port = port->p_ctl_port;
1042278037Smav		lm.plun = i;
1043278037Smav		lm.lun = targ->t_luns[i]->l_ctl_lun;
1044278037Smav		error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
1045278037Smav		if (error != 0)
1046278037Smav			log_warn("CTL_LUN_MAP ioctl failed");
1047278037Smav	}
1048278037Smav
1049278037Smav	/* Enable port */
1050255678Strasz	bzero(&entry, sizeof(entry));
1051278322Smav	entry.targ_port = port->p_ctl_port;
1052255570Strasz	error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry);
1053255570Strasz	if (error != 0) {
1054255570Strasz		log_warn("CTL_ENABLE_PORT ioctl failed");
1055255570Strasz		return (-1);
1056255570Strasz	}
1057255570Strasz
1058255570Strasz	return (0);
1059255570Strasz}
1060255570Strasz
1061255570Straszint
1062287757Smavkernel_port_update(struct port *port, struct port *oport)
1063278037Smav{
1064278037Smav	struct ctl_lun_map lm;
1065278322Smav	struct target *targ = port->p_target;
1066287757Smav	struct target *otarg = oport->p_target;
1067278037Smav	int error, i;
1068287757Smav	uint32_t olun;
1069278037Smav
1070278037Smav	/* Map configured LUNs and unmap others */
1071278037Smav	for (i = 0; i < MAX_LUNS; i++) {
1072278322Smav		lm.port = port->p_ctl_port;
1073278037Smav		lm.plun = i;
1074278037Smav		if (targ->t_luns[i] == NULL)
1075278037Smav			lm.lun = UINT32_MAX;
1076278037Smav		else
1077278037Smav			lm.lun = targ->t_luns[i]->l_ctl_lun;
1078287757Smav		if (otarg->t_luns[i] == NULL)
1079287757Smav			olun = UINT32_MAX;
1080287757Smav		else
1081287757Smav			olun = otarg->t_luns[i]->l_ctl_lun;
1082287757Smav		if (lm.lun == olun)
1083287757Smav			continue;
1084278037Smav		error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
1085278037Smav		if (error != 0)
1086278037Smav			log_warn("CTL_LUN_MAP ioctl failed");
1087278037Smav	}
1088278037Smav	return (0);
1089278037Smav}
1090278037Smav
1091278037Smavint
1092278322Smavkernel_port_remove(struct port *port)
1093255570Strasz{
1094278354Smav	struct ctl_port_entry entry;
1095278354Smav	struct ctl_lun_map lm;
1096268291Smav	struct ctl_req req;
1097268291Smav	char tagstr[16];
1098278322Smav	struct target *targ = port->p_target;
1099278322Smav	struct portal_group *pg = port->p_portal_group;
1100255570Strasz	int error;
1101255570Strasz
1102278354Smav	/* Disable port */
1103278354Smav	bzero(&entry, sizeof(entry));
1104278354Smav	entry.targ_port = port->p_ctl_port;
1105278354Smav	error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry);
1106255570Strasz	if (error != 0) {
1107278354Smav		log_warn("CTL_DISABLE_PORT ioctl failed");
1108278354Smav		return (-1);
1109255570Strasz	}
1110255570Strasz
1111278354Smav	/* Remove iSCSI port. */
1112278354Smav	if (port->p_portal_group) {
1113278354Smav		bzero(&req, sizeof(req));
1114278354Smav		strlcpy(req.driver, "iscsi", sizeof(req.driver));
1115278354Smav		req.reqtype = CTL_REQ_REMOVE;
1116278354Smav		req.num_args = 2;
1117278354Smav		req.args = malloc(req.num_args * sizeof(*req.args));
1118279276Smav		if (req.args == NULL)
1119279276Smav			log_err(1, "malloc");
1120278354Smav		str_arg(&req.args[0], "cfiscsi_target", targ->t_name);
1121278354Smav		snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
1122278354Smav		str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr);
1123278354Smav		error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
1124278354Smav		free(req.args);
1125278354Smav		if (error != 0) {
1126278354Smav			log_warn("error issuing CTL_PORT_REQ ioctl");
1127278354Smav			return (1);
1128278354Smav		}
1129278354Smav		if (req.status == CTL_LUN_ERROR) {
1130278354Smav			log_warnx("error returned from port removal request: %s",
1131278354Smav			    req.error_str);
1132278354Smav			return (1);
1133278354Smav		}
1134278354Smav		if (req.status != CTL_LUN_OK) {
1135278354Smav			log_warnx("unknown port removal request status %d",
1136278354Smav			    req.status);
1137278354Smav			return (1);
1138278354Smav		}
1139278354Smav	} else {
1140278354Smav		/* Disable LUN mapping. */
1141278354Smav		lm.port = port->p_ctl_port;
1142278354Smav		lm.plun = UINT32_MAX;
1143278354Smav		lm.lun = UINT32_MAX;
1144278354Smav		error = ioctl(ctl_fd, CTL_LUN_MAP, &lm);
1145278354Smav		if (error != 0)
1146278354Smav			log_warn("CTL_LUN_MAP ioctl failed");
1147268291Smav	}
1148255570Strasz	return (0);
1149255570Strasz}
1150255570Strasz
1151255570Strasz#ifdef ICL_KERNEL_PROXY
1152255570Straszvoid
1153264526Straszkernel_listen(struct addrinfo *ai, bool iser, int portal_id)
1154255570Strasz{
1155255570Strasz	struct ctl_iscsi req;
1156255570Strasz
1157255570Strasz	bzero(&req, sizeof(req));
1158255570Strasz
1159255570Strasz	req.type = CTL_ISCSI_LISTEN;
1160255570Strasz	req.data.listen.iser = iser;
1161255570Strasz	req.data.listen.domain = ai->ai_family;
1162255570Strasz	req.data.listen.socktype = ai->ai_socktype;
1163255570Strasz	req.data.listen.protocol = ai->ai_protocol;
1164255570Strasz	req.data.listen.addr = ai->ai_addr;
1165255570Strasz	req.data.listen.addrlen = ai->ai_addrlen;
1166264526Strasz	req.data.listen.portal_id = portal_id;
1167255570Strasz
1168264526Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
1169261765Strasz		log_err(1, "error issuing CTL_ISCSI ioctl");
1170261765Strasz
1171261765Strasz	if (req.status != CTL_ISCSI_OK) {
1172261765Strasz		log_errx(1, "error returned from CTL iSCSI listen: %s",
1173261765Strasz		    req.error_str);
1174261765Strasz	}
1175255570Strasz}
1176255570Strasz
1177264526Straszvoid
1178264530Straszkernel_accept(int *connection_id, int *portal_id,
1179264530Strasz    struct sockaddr *client_sa, socklen_t *client_salen)
1180255570Strasz{
1181255570Strasz	struct ctl_iscsi req;
1182264530Strasz	struct sockaddr_storage ss;
1183255570Strasz
1184255570Strasz	bzero(&req, sizeof(req));
1185255570Strasz
1186255570Strasz	req.type = CTL_ISCSI_ACCEPT;
1187264530Strasz	req.data.accept.initiator_addr = (struct sockaddr *)&ss;
1188255570Strasz
1189264526Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1)
1190264526Strasz		log_err(1, "error issuing CTL_ISCSI ioctl");
1191255570Strasz
1192261765Strasz	if (req.status != CTL_ISCSI_OK) {
1193264526Strasz		log_errx(1, "error returned from CTL iSCSI accept: %s",
1194261765Strasz		    req.error_str);
1195261765Strasz	}
1196261765Strasz
1197264526Strasz	*connection_id = req.data.accept.connection_id;
1198264526Strasz	*portal_id = req.data.accept.portal_id;
1199264530Strasz	*client_salen = req.data.accept.initiator_addrlen;
1200264530Strasz	memcpy(client_sa, &ss, *client_salen);
1201255570Strasz}
1202255570Strasz
1203255570Straszvoid
1204255570Straszkernel_send(struct pdu *pdu)
1205255570Strasz{
1206255570Strasz	struct ctl_iscsi req;
1207255570Strasz
1208255570Strasz	bzero(&req, sizeof(req));
1209255570Strasz
1210255570Strasz	req.type = CTL_ISCSI_SEND;
1211255570Strasz	req.data.send.connection_id = pdu->pdu_connection->conn_socket;
1212255570Strasz	req.data.send.bhs = pdu->pdu_bhs;
1213255570Strasz	req.data.send.data_segment_len = pdu->pdu_data_len;
1214255570Strasz	req.data.send.data_segment = pdu->pdu_data;
1215255570Strasz
1216261765Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
1217255570Strasz		log_err(1, "error issuing CTL_ISCSI ioctl; "
1218255570Strasz		    "dropping connection");
1219261765Strasz	}
1220255570Strasz
1221261765Strasz	if (req.status != CTL_ISCSI_OK) {
1222255570Strasz		log_errx(1, "error returned from CTL iSCSI send: "
1223255570Strasz		    "%s; dropping connection", req.error_str);
1224261765Strasz	}
1225255570Strasz}
1226255570Strasz
1227255570Straszvoid
1228255570Straszkernel_receive(struct pdu *pdu)
1229255570Strasz{
1230255570Strasz	struct ctl_iscsi req;
1231255570Strasz
1232255570Strasz	pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH);
1233255570Strasz	if (pdu->pdu_data == NULL)
1234255570Strasz		log_err(1, "malloc");
1235255570Strasz
1236255570Strasz	bzero(&req, sizeof(req));
1237255570Strasz
1238255570Strasz	req.type = CTL_ISCSI_RECEIVE;
1239255570Strasz	req.data.receive.connection_id = pdu->pdu_connection->conn_socket;
1240255570Strasz	req.data.receive.bhs = pdu->pdu_bhs;
1241255570Strasz	req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH;
1242255570Strasz	req.data.receive.data_segment = pdu->pdu_data;
1243255570Strasz
1244261765Strasz	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
1245255570Strasz		log_err(1, "error issuing CTL_ISCSI ioctl; "
1246255570Strasz		    "dropping connection");
1247261765Strasz	}
1248255570Strasz
1249261765Strasz	if (req.status != CTL_ISCSI_OK) {
1250255570Strasz		log_errx(1, "error returned from CTL iSCSI receive: "
1251255570Strasz		    "%s; dropping connection", req.error_str);
1252261765Strasz	}
1253255570Strasz
1254255570Strasz}
1255255570Strasz
1256255570Strasz#endif /* ICL_KERNEL_PROXY */
1257255570Strasz
1258255570Strasz/*
1259255570Strasz * XXX: I CANT INTO LATIN
1260255570Strasz */
1261255570Straszvoid
1262255570Straszkernel_capsicate(void)
1263255570Strasz{
1264255570Strasz	int error;
1265255570Strasz	cap_rights_t rights;
1266255570Strasz	const unsigned long cmds[] = { CTL_ISCSI };
1267255570Strasz
1268255570Strasz	cap_rights_init(&rights, CAP_IOCTL);
1269255570Strasz	error = cap_rights_limit(ctl_fd, &rights);
1270255570Strasz	if (error != 0 && errno != ENOSYS)
1271255570Strasz		log_err(1, "cap_rights_limit");
1272255570Strasz
1273317062Saraujo	error = cap_ioctls_limit(ctl_fd, cmds, nitems(cmds));
1274317062Saraujo
1275255570Strasz	if (error != 0 && errno != ENOSYS)
1276255570Strasz		log_err(1, "cap_ioctls_limit");
1277255570Strasz
1278255570Strasz	error = cap_enter();
1279255570Strasz	if (error != 0 && errno != ENOSYS)
1280255570Strasz		log_err(1, "cap_enter");
1281255570Strasz
1282255570Strasz	if (cap_sandboxed())
1283255570Strasz		log_debugx("Capsicum capability mode enabled");
1284255570Strasz	else
1285255570Strasz		log_warnx("Capsicum capability mode not supported");
1286255570Strasz}
1287255570Strasz
1288