Blame usr/discovery.c

Packit eace71
/*
Packit eace71
 * iSCSI Discovery
Packit eace71
 *
Packit eace71
 * Copyright (C) 2002 Cisco Systems, Inc.
Packit eace71
 *
Packit eace71
 * This program is free software; you can redistribute it and/or modify
Packit eace71
 * it under the terms of the GNU General Public License as published
Packit eace71
 * by the Free Software Foundation; either version 2 of the License, or
Packit eace71
 * (at your option) any later version.
Packit eace71
 *
Packit eace71
 * This program is distributed in the hope that it will be useful, but
Packit eace71
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit eace71
 * General Public License for more details.
Packit eace71
 *
Packit eace71
 * See the file COPYING included with this distribution for more details.
Packit eace71
 */
Packit eace71
Packit eace71
#include <stdio.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <fcntl.h>
Packit eace71
#include <signal.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <netdb.h>
Packit eace71
#include <stdint.h>
Packit eace71
#include <stdlib.h>
Packit eace71
#include <string.h>
Packit eace71
#include <sys/poll.h>
Packit eace71
#include <sys/time.h>
Packit eace71
#include <sys/param.h>
Packit eace71
#include <sys/socket.h>
Packit eace71
#include <netinet/in.h>
Packit eace71
#include <arpa/inet.h>
Packit eace71
Packit eace71
#include "local_strings.h"
Packit eace71
#include "types.h"
Packit eace71
#include "iscsi_proto.h"
Packit eace71
#include "initiator.h"
Packit Service 37dbff
#include "config.h"
Packit eace71
#include "log.h"
Packit eace71
#include "idbm.h"
Packit eace71
#include "iscsi_settings.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
#include "fw_context.h"
Packit eace71
#include "iscsid_req.h"
Packit eace71
#include "iscsi_util.h"
Packit eace71
#include "transport.h"
Packit eace71
#include "iscsi_sysfs.h"
Packit eace71
#include "iscsi_ipc.h"
Packit eace71
#include "iface.h"
Packit eace71
#include "iscsi_timer.h"
Packit eace71
#include "iscsi_err.h"
Packit Service 37dbff
#ifdef ISNS_ENABLE
Packit eace71
/* libisns includes */
Packit eace71
#include <libisns/isns.h>
Packit eace71
#include <libisns/paths.h>
Packit eace71
#include <libisns/message.h>
Packit Service 37dbff
#endif
Packit eace71
Packit eace71
#ifdef SLP_ENABLE
Packit eace71
#include "iscsi-slp-discovery.h"
Packit eace71
#endif
Packit eace71
Packit eace71
#define DISCOVERY_NEED_RECONNECT 0xdead0001
Packit eace71
Packit eace71
static char initiator_name[TARGET_NAME_MAXLEN + 1];
Packit eace71
static char initiator_alias[TARGET_NAME_MAXLEN + 1];
Packit eace71
static struct iscsi_ev_context ipc_ev_context;
Packit eace71
Packit eace71
static int request_initiator_name(int tmo)
Packit eace71
{
Packit eace71
	int rc;
Packit eace71
	iscsiadm_req_t req;
Packit eace71
	iscsiadm_rsp_t rsp;
Packit eace71
Packit eace71
	memset(initiator_name, 0, sizeof(initiator_name));
Packit eace71
	initiator_name[0] = '\0';
Packit eace71
	memset(initiator_alias, 0, sizeof(initiator_alias));
Packit eace71
	initiator_alias[0] = '\0';
Packit eace71
Packit eace71
	memset(&req, 0, sizeof(req));
Packit eace71
	req.command = MGMT_IPC_CONFIG_INAME;
Packit eace71
Packit eace71
	rc = iscsid_exec_req(&req, &rsp, 1, tmo);
Packit eace71
	if (rc)
Packit eace71
		return rc;
Packit eace71
Packit eace71
	if (rsp.u.config.var[0] != '\0')
Packit eace71
		strcpy(initiator_name, rsp.u.config.var);
Packit eace71
Packit eace71
	memset(&req, 0, sizeof(req));
Packit eace71
	req.command = MGMT_IPC_CONFIG_IALIAS;
Packit eace71
Packit eace71
	rc = iscsid_exec_req(&req, &rsp, 0, tmo);
Packit eace71
	if (rc)
Packit eace71
		/* alias is optional so return ok */
Packit eace71
		return 0;
Packit eace71
Packit eace71
	if (rsp.u.config.var[0] != '\0')
Packit eace71
		strcpy(initiator_alias, rsp.u.config.var);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit Service 37dbff
#ifdef ISNS_ENABLE
Packit eace71
void discovery_isns_free_servername(void)
Packit eace71
{
Packit eace71
	if (isns_config.ic_server_name)
Packit eace71
		free(isns_config.ic_server_name);
Packit eace71
	isns_config.ic_server_name = NULL;
Packit eace71
}
Packit eace71
Packit eace71
int discovery_isns_set_servername(char *address, int port)
Packit eace71
{
Packit eace71
	char *server;
Packit eace71
	int len;
Packit eace71
Packit eace71
	if (port > USHRT_MAX) {
Packit eace71
		log_error("Invalid port %d", port);
Packit eace71
		return ISCSI_ERR_INVAL;
Packit eace71
	}
Packit eace71
Packit eace71
	/* 5 for port and 1 for colon and 1 for null */
Packit eace71
	len = strlen(address) + 7;
Packit eace71
	server = calloc(1, len);
Packit eace71
	if (!server)
Packit eace71
		return ISCSI_ERR_NOMEM;
Packit eace71
Packit eace71
	snprintf(server, len, "%s:%d", address, port);
Packit eace71
	isns_assign_string(&isns_config.ic_server_name, server);
Packit eace71
	free(server);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
int discovery_isns_query(struct discovery_rec *drec, const char *iname,
Packit eace71
			 const char *targetname, struct list_head *rec_list)
Packit eace71
{
Packit eace71
	isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
Packit eace71
	isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
Packit eace71
	isns_source_t *source;
Packit eace71
	isns_simple_t *qry;
Packit eace71
	isns_client_t *clnt;
Packit eace71
	uint32_t status;
Packit eace71
	int rc, i;
Packit eace71
Packit eace71
	isns_config.ic_security = 0;
Packit eace71
	source = isns_source_create_iscsi(iname);
Packit eace71
	if (!source)
Packit eace71
		return ISCSI_ERR_NOMEM;
Packit eace71
Packit eace71
	clnt = isns_create_client(NULL, iname); 
Packit eace71
	if (!clnt) {
Packit eace71
		rc = ISCSI_ERR_NOMEM;
Packit eace71
		goto free_src;
Packit eace71
	}
Packit eace71
Packit eace71
	/* do not retry forever */
Packit eace71
	isns_socket_set_disconnect_fatal(clnt->ic_socket);
Packit eace71
Packit eace71
	if (targetname)
Packit eace71
		isns_attr_list_append_string(&key_attrs, ISNS_TAG_ISCSI_NAME,
Packit eace71
					     targetname);
Packit eace71
	else
Packit eace71
		/* Query for all visible targets */
Packit eace71
		isns_attr_list_append_uint32(&key_attrs,
Packit eace71
					     ISNS_TAG_ISCSI_NODE_TYPE,
Packit eace71
					     ISNS_ISCSI_TARGET_MASK);
Packit eace71
Packit eace71
	qry = isns_create_query2(clnt, &key_attrs, source);
Packit eace71
	if (!qry) {
Packit eace71
		rc = ISCSI_ERR_NOMEM;
Packit eace71
		goto free_clnt;
Packit eace71
	}
Packit eace71
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_ISCSI_NAME);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_ISCSI_NODE_TYPE);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PORTAL_IP_ADDRESS);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PORTAL_TCP_UDP_PORT);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PG_ISCSI_NAME);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PG_PORTAL_IP_ADDR);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT);
Packit eace71
	isns_query_request_attr_tag(qry, ISNS_TAG_PG_TAG);
Packit eace71
Packit eace71
	status = isns_client_call(clnt, &qry);
Packit eace71
	switch (status) {
Packit eace71
	case ISNS_SUCCESS:
Packit eace71
		break;
Packit eace71
	case ISNS_SOURCE_UNKNOWN:
Packit eace71
		/* server requires that we are registered but we are not */
Packit eace71
		rc = ISCSI_ERR_ISNS_REG_FAILED;
Packit eace71
		goto free_query;
Packit eace71
	default:
Packit eace71
		log_error("iSNS discovery failed: %s", isns_strerror(status));
Packit eace71
		rc = ISCSI_ERR_ISNS_QUERY;
Packit eace71
		goto free_query;
Packit eace71
	}
Packit eace71
Packit eace71
	status = isns_query_response_get_objects(qry, &objects);
Packit eace71
	if (status) {
Packit eace71
		log_error("Unable to extract object list from query "
Packit eace71
			  "response: %s", isns_strerror(status));
Packit eace71
		rc = ISCSI_ERR;
Packit eace71
		goto free_query;
Packit eace71
	}
Packit eace71
Packit eace71
	for (i = 0; i < objects.iol_count; ++i) {
Packit eace71
		isns_object_t *obj = objects.iol_data[i];
Packit eace71
		const char *pg_tgt = NULL;
Packit eace71
		struct in6_addr in_addr;
Packit eace71
		uint32_t pg_port = ISCSI_LISTEN_PORT;
Packit eace71
		uint32_t pg_tag = PORTAL_GROUP_TAG_UNKNOWN;
Packit eace71
		char pg_addr[INET6_ADDRSTRLEN + 1];
Packit eace71
		struct node_rec *rec;
Packit eace71
Packit eace71
		if (!isns_object_is_pg(obj))
Packit eace71
			continue;
Packit eace71
Packit eace71
		if (!isns_object_get_string(obj, ISNS_TAG_PG_ISCSI_NAME,
Packit eace71
					    &pg_tgt)) {
Packit eace71
			log_debug(1, "Missing target name");
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		if (!isns_object_get_ipaddr(obj, ISNS_TAG_PG_PORTAL_IP_ADDR,
Packit eace71
					    &in_addr)) {
Packit eace71
			log_debug(1, "Missing addr");
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
		if (IN6_IS_ADDR_V4MAPPED(&in_addr) ||
Packit eace71
		    IN6_IS_ADDR_V4COMPAT(&in_addr)) {
Packit eace71
			struct in_addr ipv4;
Packit eace71
Packit eace71
			ipv4.s_addr = in_addr.s6_addr32[3];
Packit eace71
			inet_ntop(AF_INET, &ipv4, pg_addr, sizeof(pg_addr));
Packit eace71
		} else
Packit eace71
			inet_ntop(AF_INET6, &in_addr, pg_addr, sizeof(pg_addr));
Packit eace71
Packit eace71
		if (!isns_object_get_uint32(obj,
Packit eace71
					    ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
Packit eace71
					    &pg_port)) {
Packit eace71
			log_debug(1, "Missing port");
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		if (!isns_object_get_uint32(obj, ISNS_TAG_PG_TAG, &pg_tag)) {
Packit eace71
			log_debug(1, "Missing tag");
Packit eace71
			continue;
Packit eace71
		}
Packit eace71
Packit eace71
		rec = calloc(1, sizeof(*rec));
Packit eace71
		if (!rec) {
Packit eace71
			rc = ISCSI_ERR_NOMEM;
Packit eace71
			goto destroy_list;
Packit eace71
		}
Packit eace71
Packit eace71
		idbm_node_setup_from_conf(rec);
Packit eace71
		if (drec) {
Packit eace71
			rec->disc_type = drec->type;
Packit eace71
			rec->disc_port = drec->port;
Packit eace71
			strcpy(rec->disc_address, drec->address);
Packit eace71
		}
Packit eace71
Packit eace71
		strlcpy(rec->name, pg_tgt, TARGET_NAME_MAXLEN);
Packit eace71
		rec->tpgt = pg_tag;
Packit eace71
		rec->conn[0].port = pg_port;
Packit eace71
		strlcpy(rec->conn[0].address, pg_addr, NI_MAXHOST);
Packit eace71
		list_add_tail(&rec->list, rec_list);
Packit eace71
	}
Packit eace71
	rc = 0;
Packit eace71
Packit eace71
	isns_flush_events();
Packit eace71
destroy_list:
Packit eace71
	isns_object_list_destroy(&objects);
Packit eace71
free_query:
Packit eace71
	isns_simple_free(qry);
Packit eace71
free_clnt:
Packit eace71
	isns_client_destroy(clnt);
Packit eace71
free_src:
Packit eace71
	isns_source_release(source);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * discovery_isns_reg_node - register/deregister node
Packit eace71
 * @iname: initiator name
Packit eace71
 * @reg: bool indicating if we are supposed to register or deregister node.
Packit eace71
 *
Packit eace71
 * We do a very simple registration just so we can query.
Packit eace71
 */
Packit eace71
static int discovery_isns_reg_node(const char *iname, int op_reg)
Packit eace71
{
Packit eace71
	isns_simple_t *reg;
Packit eace71
	isns_client_t *clnt;
Packit eace71
	isns_source_t *source;
Packit eace71
	int rc = 0, status;
Packit eace71
Packit eace71
	isns_config.ic_security = 0;
Packit eace71
Packit eace71
	log_debug(1, "trying to %s %s with iSNS server.",
Packit eace71
		  op_reg ? "register" : "deregister", iname);
Packit eace71
Packit eace71
	source = isns_source_create_iscsi(iname);
Packit eace71
	if (!source)
Packit eace71
		return ISCSI_ERR_NOMEM;
Packit eace71
Packit eace71
	clnt = isns_create_client(NULL, iname); 
Packit eace71
	if (!clnt) {
Packit eace71
		rc = ISCSI_ERR_NOMEM;
Packit eace71
		goto free_src;
Packit eace71
	}
Packit eace71
Packit eace71
	reg = isns_simple_create(op_reg ? ISNS_DEVICE_ATTRIBUTE_REGISTER :
Packit eace71
				 ISNS_DEVICE_DEREGISTER,
Packit eace71
				 source, NULL);
Packit eace71
	if (!reg) {
Packit eace71
		rc = ISCSI_ERR_NOMEM;
Packit eace71
		goto free_clnt;
Packit eace71
	}
Packit eace71
Packit eace71
	isns_attr_list_append_string(&reg->is_operating_attrs,
Packit eace71
				     ISNS_TAG_ISCSI_NAME, iname);
Packit eace71
	if (op_reg)
Packit eace71
		isns_attr_list_append_uint32(&reg->is_operating_attrs,
Packit eace71
					     ISNS_TAG_ISCSI_NODE_TYPE,
Packit eace71
					     ISNS_ISCSI_INITIATOR_MASK);
Packit eace71
	status = isns_client_call(clnt, ®);
Packit eace71
	if (status != ISNS_SUCCESS) {
Packit eace71
		log_error("Could not %s %s with iSNS server: %s.",
Packit eace71
			  reg ? "register" : "deregister", iname,
Packit eace71
			  isns_strerror(status));
Packit eace71
		rc = ISCSI_ERR_ISNS_REG_FAILED;
Packit eace71
	} else
Packit eace71
		log_debug(1, "%s %s with iSNS server successful.",
Packit eace71
			  op_reg ? "register" : "deregister", iname);
Packit eace71
free_clnt:
Packit eace71
	isns_client_destroy(clnt);
Packit eace71
free_src:
Packit eace71
	isns_source_release(source);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
int discovery_isns(void *data, struct iface_rec *iface,
Packit eace71
		   struct list_head *rec_list)
Packit eace71
{
Packit eace71
	struct discovery_rec *drec = data;
Packit eace71
	char *iname;
Packit eace71
	int rc, registered = 0;
Packit eace71
Packit eace71
	if (iface && strlen(iface->iname))
Packit eace71
		iname = iface->iname;
Packit eace71
	else {
Packit eace71
		rc = request_initiator_name(drec->iscsid_req_tmo);
Packit eace71
		if (rc) {
Packit eace71
			log_error("Cannot perform discovery. Initiatorname "
Packit eace71
				  "required.");
Packit eace71
			return rc;
Packit eace71
		} else if (initiator_name[0] == '\0') {
Packit eace71
			log_error("Cannot perform discovery. Invalid "
Packit eace71
				  "Initiatorname.");
Packit eace71
			return ISCSI_ERR_INVAL;
Packit eace71
		}
Packit eace71
Packit eace71
		iname = initiator_name;
Packit eace71
	}
Packit eace71
Packit eace71
	rc = discovery_isns_set_servername(drec->address, drec->port);
Packit eace71
	if (rc)
Packit eace71
		return rc;
Packit eace71
retry:
Packit eace71
	rc = discovery_isns_query(drec, iname, NULL, rec_list);
Packit eace71
	if (!registered && rc == ISCSI_ERR_ISNS_REG_FAILED) {
Packit eace71
		rc = discovery_isns_reg_node(iname, 1);
Packit eace71
		if (!rc) {
Packit eace71
			registered = 1;
Packit eace71
			goto retry;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	if (registered)
Packit eace71
		discovery_isns_reg_node(iname, 0);
Packit eace71
Packit eace71
	discovery_isns_free_servername();
Packit eace71
	return rc;
Packit eace71
}
Packit Service 37dbff
#endif
Packit eace71
Packit eace71
int discovery_fw(void *data, struct iface_rec *iface,
Packit eace71
		 struct list_head *rec_list)
Packit eace71
{
Packit eace71
	struct discovery_rec *drec = data;
Packit eace71
	struct boot_context *bcontext;
Packit eace71
	struct list_head targets;
Packit eace71
	struct node_rec *rec;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&targets;;
Packit eace71
	rc = fw_get_targets(&targets;;
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not get list of targets from firmware. "
Packit eace71
			  "(err %d)", rc);
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
	if (list_empty(&targets))
Packit eace71
		return 0;
Packit eace71
	/*
Packit eace71
	 * TODO: Do we want to match the iface MAC/netdev with what is in
Packit eace71
	 * the firmware or could the user want to bind based on what is
Packit eace71
	 * in passed in or in the default ifaces?
Packit eace71
	 */
Packit eace71
Packit eace71
	list_for_each_entry(bcontext, &targets, list) {
Packit eace71
		rec = idbm_create_rec_from_boot_context(bcontext);
Packit eace71
		if (!rec) {
Packit eace71
			log_error("Could not convert firmware info to "
Packit eace71
				  "node record.");
Packit eace71
			rc = ISCSI_ERR_NOMEM;
Packit eace71
			goto free_targets;
Packit eace71
		}
Packit eace71
		rec->disc_type = drec->type;
Packit eace71
Packit eace71
		list_add_tail(&rec->list, rec_list);
Packit eace71
	}
Packit eace71
Packit eace71
free_targets:
Packit eace71
	fw_free_targets(&targets;;
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
int discovery_offload_sendtargets(int host_no, int do_login,
Packit eace71
				  discovery_rec_t *drec)
Packit eace71
{
Packit eace71
	struct sockaddr_storage ss;
Packit eace71
	char default_port[NI_MAXSERV];
Packit eace71
	iscsiadm_req_t req;
Packit eace71
	iscsiadm_rsp_t rsp;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	log_debug(4, "offload st though host %d to %s", host_no,
Packit eace71
		  drec->address);
Packit eace71
Packit eace71
	memset(&req, 0, sizeof(req));
Packit eace71
	req.command = MGMT_IPC_SEND_TARGETS;
Packit eace71
	req.u.st.host_no = host_no;
Packit eace71
	req.u.st.do_login = do_login;
Packit eace71
Packit eace71
	/* resolve the DiscoveryAddress to an IP address */
Packit eace71
	sprintf(default_port, "%d", drec->port);
Packit eace71
	rc = resolve_address(drec->address, default_port, &ss);
Packit eace71
	if (rc)
Packit eace71
		return rc;
Packit eace71
Packit eace71
	req.u.st.ss = ss;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * We only know how ask qla4xxx to do discovery and login
Packit eace71
	 * to what it finds. We are not able to get what it finds or
Packit eace71
	 * is able to log into so we just send the command and proceed.
Packit eace71
	 *
Packit eace71
	 * There is a way to just use the hw to send a sendtargets command
Packit eace71
	 * and get back the results. We should do this since it would
Packit eace71
	 * allows us to then process the results like software iscsi.
Packit eace71
	 */
Packit eace71
	rc = iscsid_exec_req(&req, &rsp, 1, drec->iscsid_req_tmo);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not offload sendtargets to %s.",
Packit eace71
			  drec->address);
Packit eace71
		iscsi_err_print_msg(rc);
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
iscsi_make_text_pdu(iscsi_session_t *session, struct iscsi_hdr *hdr,
Packit eace71
		    char *data, int max_data_length)
Packit eace71
{
Packit eace71
	struct iscsi_text *text_pdu = (struct iscsi_text *)hdr;
Packit eace71
Packit eace71
	/* initialize the PDU header */
Packit eace71
	memset(text_pdu, 0, sizeof (*text_pdu));
Packit eace71
Packit eace71
	text_pdu->opcode = ISCSI_OP_TEXT;
Packit eace71
	text_pdu->itt = htonl(session->itt);
Packit eace71
	text_pdu->ttt = ISCSI_RESERVED_TAG;
Packit eace71
	text_pdu->cmdsn = htonl(session->cmdsn++);
Packit eace71
	text_pdu->exp_statsn = htonl(session->conn[0].exp_statsn);
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
request_targets(iscsi_session_t *session)
Packit eace71
{
Packit eace71
	char data[64];
Packit eace71
	struct iscsi_text text;
Packit eace71
	struct iscsi_hdr *hdr = (struct iscsi_hdr *) &tex;;
Packit eace71
Packit eace71
	memset(&text, 0, sizeof (text));
Packit eace71
	memset(data, 0, sizeof (data));
Packit eace71
Packit eace71
	/* make a text PDU with SendTargets=All */
Packit eace71
	if (!iscsi_make_text_pdu(session, hdr, data, sizeof (data))) {
Packit eace71
		log_error("failed to make a SendTargets PDU");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!iscsi_add_text(hdr, data, sizeof (data), "SendTargets", "All")) {
Packit eace71
		log_error("failed to add SendTargets text key");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	text.ttt = ISCSI_RESERVED_TAG;
Packit eace71
	text.flags = ISCSI_FLAG_CMD_FINAL;
Packit eace71
Packit eace71
	if (!iscsi_io_send_pdu(&session->conn[0], hdr, ISCSI_DIGEST_NONE, data,
Packit eace71
		    ISCSI_DIGEST_NONE, session->conn[0].active_timeout)) {
Packit eace71
		log_error("failed to send SendTargets PDU");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
iterate_targets(iscsi_session_t *session, uint32_t ttt)
Packit eace71
{
Packit eace71
	char data[64];
Packit eace71
	struct iscsi_text text;
Packit eace71
	struct iscsi_hdr *pdu = (struct iscsi_hdr *) &tex;;
Packit eace71
Packit eace71
	memset(&text, 0, sizeof (text));
Packit eace71
	memset(data, 0, sizeof (data));
Packit eace71
Packit eace71
	/* make an empty text PDU */
Packit eace71
	if (!iscsi_make_text_pdu(session, pdu, data, sizeof (data))) {
Packit eace71
		log_error("failed to make an empty text PDU");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	text.ttt = ttt;
Packit eace71
	text.flags = ISCSI_FLAG_CMD_FINAL;
Packit eace71
Packit eace71
	if (!iscsi_io_send_pdu(&session->conn[0], pdu, ISCSI_DIGEST_NONE, data,
Packit eace71
		    ISCSI_DIGEST_NONE, session->conn[0].active_timeout)) {
Packit eace71
		log_error("failed to send empty text PDU");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int add_portal(struct list_head *rec_list, discovery_rec_t *drec,
Packit eace71
		      char *targetname, char *address, char *port, char *tag)
Packit eace71
{
Packit eace71
	struct sockaddr_storage ss;
Packit eace71
	struct node_rec *rec;
Packit eace71
Packit eace71
	if (resolve_address(address, port, &ss)) {
Packit eace71
		log_error("cannot resolve %s", address);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	rec = calloc(1, sizeof(*rec));
Packit eace71
	if (!rec)
Packit eace71
		return 0;
Packit eace71
Packit eace71
	idbm_node_setup_from_conf(rec);
Packit eace71
	rec->disc_type = drec->type;
Packit eace71
	rec->disc_port = drec->port;
Packit eace71
	strcpy(rec->disc_address, drec->address);
Packit eace71
Packit eace71
	strlcpy(rec->name, targetname, TARGET_NAME_MAXLEN);
Packit eace71
	if (tag && *tag)
Packit eace71
		rec->tpgt = atoi(tag);
Packit eace71
	else
Packit eace71
		rec->tpgt = PORTAL_GROUP_TAG_UNKNOWN;
Packit eace71
	if (port && *port)
Packit eace71
		rec->conn[0].port = atoi(port);
Packit eace71
	else
Packit eace71
		rec->conn[0].port = ISCSI_LISTEN_PORT;
Packit eace71
	strlcpy(rec->conn[0].address, address, NI_MAXHOST);
Packit eace71
Packit eace71
	list_add_tail(&rec->list, rec_list);
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
add_target_record(char *name, char *end, discovery_rec_t *drec,
Packit eace71
		  struct list_head *rec_list)
Packit eace71
{
Packit eace71
	char *text = NULL;
Packit eace71
	char *nul = name;
Packit eace71
	size_t length;
Packit eace71
Packit eace71
	/* address = IPv4
Packit eace71
	 * address = [IPv6]
Packit eace71
	 * address = DNSname
Packit eace71
	 * address = IPv4:port
Packit eace71
	 * address = [IPv6]:port
Packit eace71
	 * address = DNSname:port
Packit eace71
	 * address = IPv4,tag
Packit eace71
	 * address = [IPv6],tag
Packit eace71
	 * address = DNSname,tag
Packit eace71
	 * address = IPv4:port,tag
Packit eace71
	 * address = [IPv6]:port,tag
Packit eace71
	 * address = DNSname:port,tag
Packit eace71
	 */
Packit eace71
Packit eace71
	log_debug(7, "adding target record %p, end %p", name, end);
Packit eace71
Packit eace71
	/* find the end of the name */
Packit eace71
	while ((nul < end) && (*nul != '\0'))
Packit eace71
		nul++;
Packit eace71
Packit eace71
	length = nul - name;
Packit eace71
	if (length > TARGET_NAME_MAXLEN) {
Packit eace71
		log_error("TargetName %s too long, ignoring", name);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
	text = name + length;
Packit eace71
Packit eace71
	/* skip NULs after the name */
Packit eace71
	while ((text < end) && (*text == '\0'))
Packit eace71
		text++;
Packit eace71
Packit eace71
	/* if no address is provided, use the default */
Packit eace71
	if (text >= end) {
Packit eace71
		if (drec->address == NULL) {
Packit eace71
			log_error("no default address known for target %s",
Packit eace71
				  name);
Packit eace71
			return 0;
Packit eace71
		} else {
Packit eace71
			char default_port[NI_MAXSERV];
Packit eace71
Packit eace71
			sprintf(default_port, "%d", drec->port);
Packit eace71
			if (!add_portal(rec_list, drec, name, drec->address,
Packit eace71
				        default_port, NULL)) {
Packit eace71
				log_error("failed to add default portal, "
Packit eace71
					  "ignoring target %s", name);
Packit eace71
				return 0;
Packit eace71
			}
Packit eace71
		}
Packit eace71
		/* finished adding the default */
Packit eace71
		return 1;
Packit eace71
	}
Packit eace71
Packit eace71
	/* process TargetAddresses */
Packit eace71
	while (text < end) {
Packit eace71
		char *next = text + strlen(text) + 1;
Packit eace71
Packit eace71
		log_debug(7, "text %p, next %p, end %p, %s", text, next, end,
Packit eace71
			 text);
Packit eace71
Packit eace71
		if (strncmp(text, "TargetAddress=", 14) == 0) {
Packit eace71
			char *port = NULL;
Packit eace71
			char *tag = NULL;
Packit eace71
			char *address = text + 14;
Packit eace71
			char *temp;
Packit eace71
Packit eace71
			if ((tag = strrchr(text, ','))) {
Packit eace71
				*tag = '\0';
Packit eace71
				tag++;
Packit eace71
			}
Packit eace71
			if ((port = strrchr(text, ':'))) {
Packit eace71
				*port = '\0';
Packit eace71
				port++;
Packit eace71
			}
Packit eace71
Packit eace71
			if (*address == '[') {
Packit eace71
				address++;
Packit eace71
				if ((temp = strrchr(text, ']')))
Packit eace71
					*temp = '\0';
Packit eace71
			}
Packit eace71
Packit eace71
			if (!add_portal(rec_list, drec, name, address, port,
Packit eace71
					tag)) {
Packit eace71
				log_error("failed to add default portal, "
Packit eace71
					 "ignoring target %s", name);
Packit eace71
				return 0;
Packit eace71
			}
Packit eace71
		} else
Packit eace71
			log_error("unexpected SendTargets data: %s",
Packit eace71
			       text);
Packit eace71
		text = next;
Packit eace71
	}
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
process_sendtargets_response(struct str_buffer *sendtargets,
Packit eace71
			     int final, discovery_rec_t *drec,
Packit eace71
			     struct list_head *rec_list)
Packit eace71
{
Packit eace71
	char *start = str_buffer_data(sendtargets);
Packit eace71
	char *text = start;
Packit eace71
	char *end = text + str_data_length(sendtargets);
Packit eace71
	char *nul = end - 1;
Packit eace71
	char *record = NULL;
Packit eace71
	int num_targets = 0;
Packit eace71
Packit eace71
	if (start == end) {
Packit eace71
		/* no SendTargets data */
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/* scan backwards to find the last NUL in the data, to ensure we
Packit eace71
	 * don't walk off the end.  Since key=value pairs can span PDU
Packit eace71
	 * boundaries, we're not guaranteed that the end of the data has a
Packit eace71
	 * NUL.
Packit eace71
	 */
Packit eace71
	while ((nul > start) && *nul)
Packit eace71
		nul--;
Packit eace71
Packit eace71
	if (nul == start) {
Packit eace71
		/* couldn't find anything we can process now,
Packit eace71
		 * it's one big partial string
Packit eace71
		 */
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/* find the boundaries between target records (TargetName or final PDU)
Packit eace71
	 */
Packit eace71
	for (;;) {
Packit eace71
		/* skip NULs */
Packit eace71
		while ((text < nul) && (*text == '\0'))
Packit eace71
			text++;
Packit eace71
Packit eace71
		if (text == nul)
Packit eace71
			break;
Packit eace71
Packit eace71
		log_debug(7,
Packit eace71
			 "processing sendtargets record %p, text %p, line %s",
Packit eace71
			 record, text, text);
Packit eace71
Packit eace71
		/* look for the start of a new target record */
Packit eace71
		if (strncmp(text, "TargetName=", 11) == 0) {
Packit eace71
			if (record) {
Packit eace71
				/* send the last record, which we just found
Packit eace71
				 * the end of. don't bother passing the
Packit eace71
				 * "TargetName=" prefix.
Packit eace71
				 */
Packit eace71
				if (!add_target_record(record + 11, text,
Packit eace71
							drec, rec_list)) {
Packit eace71
					log_error(
Packit eace71
					       "failed to add target record");
Packit eace71
					str_truncate_buffer(sendtargets, 0);
Packit eace71
					goto done;
Packit eace71
				}
Packit eace71
				num_targets++;
Packit eace71
			}
Packit eace71
			record = text;
Packit eace71
		}
Packit eace71
Packit eace71
		/* everything up til the next NUL must be part of the
Packit eace71
		 * current target record
Packit eace71
		 */
Packit eace71
		while ((text < nul) && (*text != '\0'))
Packit eace71
			text++;
Packit eace71
	}
Packit eace71
Packit eace71
	if (record) {
Packit eace71
		if (final) {
Packit eace71
			/* if this is the last PDU of the text sequence,
Packit eace71
			 * it also ends a target record
Packit eace71
			 */
Packit eace71
			log_debug(7,
Packit eace71
				 "processing final sendtargets record %p, "
Packit eace71
				 "line %s",
Packit eace71
				 record, record);
Packit eace71
			if (add_target_record (record + 11, text,
Packit eace71
					       drec, rec_list)) {
Packit eace71
				num_targets++;
Packit eace71
				record = NULL;
Packit eace71
				str_truncate_buffer(sendtargets, 0);
Packit eace71
			} else {
Packit eace71
				log_error("failed to add target record");
Packit eace71
				str_truncate_buffer(sendtargets, 0);
Packit eace71
				goto done;
Packit eace71
			}
Packit eace71
		} else {
Packit eace71
			/* remove the parts of the sendtargets buffer we've
Packit eace71
			 * processed, and move the parts we haven't to the
Packit eace71
			 * beginning of the buffer.
Packit eace71
			 */
Packit eace71
			log_debug(7,
Packit eace71
				 "processed %d bytes of sendtargets data, "
Packit eace71
				 "%d remaining",
Packit eace71
				 (int)(record - str_buffer_data(sendtargets)),
Packit eace71
				 (int)(str_buffer_data(sendtargets) +
Packit eace71
				 str_data_length(sendtargets) - record));
Packit eace71
			str_remove_initial(sendtargets,
Packit eace71
					   record - str_buffer_data(sendtargets));
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
      done:
Packit eace71
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_free_session(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	list_del_init(&session->list);
Packit eace71
	free(session);
Packit eace71
}
Packit eace71
Packit eace71
static iscsi_session_t *
Packit eace71
iscsi_alloc_session(struct iscsi_sendtargets_config *config,
Packit eace71
		    struct iface_rec *iface, int *rc, int tmo)
Packit eace71
{
Packit eace71
	iscsi_session_t *session;
Packit eace71
Packit eace71
	*rc = 0;
Packit eace71
Packit eace71
	session = calloc(1, sizeof (*session));
Packit eace71
	if (session == NULL) {
Packit eace71
		*rc = ISCSI_ERR_NOMEM;
Packit eace71
		return NULL;
Packit eace71
	}
Packit eace71
Packit eace71
	session->t = iscsi_sysfs_get_transport_by_name(iface->transport_name);
Packit eace71
	if (!session->t) {
Packit eace71
		log_error("iSCSI driver %s is not loaded. Load the module "
Packit eace71
			  "then retry the command.", iface->transport_name);
Packit eace71
		*rc = ISCSI_ERR_TRANS_NOT_FOUND;
Packit eace71
		goto fail;
Packit eace71
	}
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&session->list);
Packit eace71
	/* initialize the session's leading connection */
Packit eace71
	session->conn[0].id = 0;
Packit eace71
	session->conn[0].socket_fd = -1;
Packit eace71
	session->conn[0].session = session;
Packit eace71
	session->conn[0].login_timeout = config->conn_timeo.login_timeout;
Packit eace71
	session->conn[0].auth_timeout = config->conn_timeo.auth_timeout;
Packit eace71
	session->conn[0].active_timeout = config->conn_timeo.active_timeout;
Packit eace71
	session->conn[0].noop_out_timeout = 0;
Packit eace71
	session->conn[0].noop_out_interval = 0;
Packit eace71
	session->reopen_cnt = config->reopen_max + 1;
Packit eace71
	iscsi_copy_operational_params(&session->conn[0], &config->session_conf,
Packit eace71
				      &config->conn_conf);
Packit eace71
Packit eace71
	/* OUI and uniqifying number */
Packit eace71
	session->isid[0] = DRIVER_ISID_0;
Packit eace71
	session->isid[1] = DRIVER_ISID_1;
Packit eace71
	session->isid[2] = DRIVER_ISID_2;
Packit eace71
	session->isid[3] = 0;
Packit eace71
	session->isid[4] = 0;
Packit eace71
	session->isid[5] = 0;
Packit eace71
Packit eace71
	if (strlen(iface->iname)) {
Packit eace71
		strcpy(initiator_name, iface->iname);
Packit eace71
		/* MNC TODO add iface alias */
Packit eace71
	} else {
Packit eace71
		*rc = request_initiator_name(tmo);
Packit eace71
		if (*rc) {
Packit eace71
			log_error("Cannot perform discovery. Initiatorname "
Packit eace71
				  "required.");
Packit eace71
			goto fail;
Packit eace71
		} else if (initiator_name[0] == '\0') {
Packit eace71
			log_error("Cannot perform discovery. Invalid "
Packit eace71
				  "Initiatorname.");
Packit eace71
			*rc = ISCSI_ERR_INVAL;
Packit eace71
			goto fail;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	iface_copy(&session->nrec.iface, iface);
Packit eace71
	session->initiator_name = initiator_name;
Packit eace71
	session->initiator_alias = initiator_alias;
Packit eace71
	session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
Packit eace71
	session->type = ISCSI_SESSION_TYPE_DISCOVERY;
Packit eace71
	session->id = -1;
Packit eace71
Packit eace71
	/* setup authentication variables for the session*/
Packit eace71
	*rc = iscsi_setup_authentication(session, &config->auth);
Packit eace71
	if (*rc)
Packit eace71
		goto fail;
Packit eace71
Packit eace71
	list_add_tail(&session->list, &session->t->sessions);
Packit eace71
	return session;
Packit eace71
Packit eace71
fail:
Packit eace71
	free(session);
Packit eace71
	return NULL;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
process_recvd_pdu(struct iscsi_hdr *pdu,
Packit eace71
		  discovery_rec_t *drec,
Packit eace71
		  struct list_head *rec_list,
Packit eace71
		  iscsi_session_t *session,
Packit eace71
		  struct str_buffer *sendtargets,
Packit eace71
		  int *active,
Packit eace71
		  int *valid_text,
Packit eace71
		  char *data)
Packit eace71
{
Packit eace71
	int rc=0;
Packit eace71
Packit eace71
	switch (pdu->opcode) {
Packit eace71
		case ISCSI_OP_TEXT_RSP:{
Packit eace71
			struct iscsi_text_rsp *text_response =
Packit eace71
				(struct iscsi_text_rsp *) pdu;
Packit eace71
			int dlength = ntoh24(pdu->dlength);
Packit eace71
			int final =
Packit eace71
				(text_response->flags & ISCSI_FLAG_CMD_FINAL) ||
Packit eace71
				(text_response-> ttt == ISCSI_RESERVED_TAG);
Packit eace71
			size_t curr_data_length;
Packit eace71
Packit eace71
			log_debug(4, "discovery session to %s:%d received text"
Packit eace71
				 " response, %d data bytes, ttt 0x%x, "
Packit eace71
				 "final 0x%x",
Packit eace71
				 drec->address,
Packit eace71
				 drec->port,
Packit eace71
				 dlength,
Packit eace71
				 ntohl(text_response->ttt),
Packit eace71
				 text_response->flags & ISCSI_FLAG_CMD_FINAL);
Packit eace71
Packit eace71
			/* mark how much more data in the sendtargets
Packit eace71
			 * buffer is now valid
Packit eace71
			 */
Packit eace71
			curr_data_length = str_data_length(sendtargets);
Packit eace71
			if (str_enlarge_data(sendtargets, dlength)) {
Packit eace71
				log_error("Could not allocate memory to "
Packit eace71
					  "process SendTargets response.");
Packit eace71
				rc = 0;
Packit eace71
				goto done;
Packit eace71
			}
Packit eace71
Packit eace71
			memcpy(str_buffer_data(sendtargets) + curr_data_length,
Packit eace71
			       data, dlength);
Packit eace71
Packit eace71
			*valid_text = 1;
Packit eace71
			/* process as much as we can right now */
Packit eace71
			process_sendtargets_response(sendtargets,
Packit eace71
						     final,
Packit eace71
						     drec,
Packit eace71
						     rec_list);
Packit eace71
Packit eace71
			if (final) {
Packit eace71
				/* SendTargets exchange is now complete
Packit eace71
				 */
Packit eace71
				*active = 0;
Packit eace71
				/* from now on, after any reconnect,
Packit eace71
				 * assume LUNs may have changed
Packit eace71
				 */
Packit eace71
			} else {
Packit eace71
				/* ask for more targets */
Packit eace71
				if (!iterate_targets(session,
Packit eace71
						     text_response->ttt)) {
Packit eace71
					rc = DISCOVERY_NEED_RECONNECT;
Packit eace71
					goto done;
Packit eace71
				}
Packit eace71
			}
Packit eace71
			break;
Packit eace71
		}
Packit eace71
		default:
Packit eace71
			log_warning(
Packit eace71
			       "discovery session to %s:%d received "
Packit eace71
			       "unexpected opcode 0x%x",
Packit eace71
			       drec->address, drec->port, pdu->opcode);
Packit eace71
			rc = DISCOVERY_NEED_RECONNECT;
Packit eace71
			goto done;
Packit eace71
	}
Packit eace71
 done:
Packit eace71
	return(rc);
Packit eace71
}
Packit eace71
Packit eace71
#if 0 /* Unused */
Packit eace71
/*
Packit eace71
 * Make a best effort to logout the session.
Packit eace71
 */
Packit eace71
static void iscsi_logout(iscsi_session_t * session)
Packit eace71
{
Packit eace71
	struct iscsi_logout logout_req;
Packit eace71
	struct iscsi_logout_rsp logout_resp;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Build logout request header
Packit eace71
	 */
Packit eace71
	memset(&logout_req, 0, sizeof (logout_req));
Packit eace71
	logout_req.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE;
Packit eace71
	logout_req.flags = ISCSI_FLAG_CMD_FINAL |
Packit eace71
		(ISCSI_LOGOUT_REASON_CLOSE_SESSION &
Packit eace71
				ISCSI_FLAG_LOGOUT_REASON_MASK);
Packit eace71
	logout_req.itt = htonl(session->itt);
Packit eace71
	if (++session->itt == ISCSI_RESERVED_TAG)
Packit eace71
		session->itt = 1;
Packit eace71
	logout_req.cmdsn = htonl(session->cmdsn);
Packit eace71
	logout_req.exp_statsn = htonl(++session->conn[0].exp_statsn);
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Send the logout request
Packit eace71
	 */
Packit eace71
	rc = iscsi_io_send_pdu(&session->conn[0],(struct iscsi_hdr *)&logout_req,
Packit eace71
			    ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 3);
Packit eace71
	if (!rc) {
Packit eace71
		log_error(
Packit eace71
		       "iscsid: iscsi_logout - failed to send logout PDU.");
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Read the logout response
Packit eace71
	 */
Packit eace71
	memset(&logout_resp, 0, sizeof(logout_resp));
Packit eace71
	rc = iscsi_io_recv_pdu(&session->conn[0],
Packit eace71
		(struct iscsi_hdr *)&logout_resp, ISCSI_DIGEST_NONE, NULL,
Packit eace71
		0, ISCSI_DIGEST_NONE, 1);
Packit eace71
	if (rc < 0) {
Packit eace71
		log_error("iscsid: logout - failed to receive logout resp");
Packit eace71
		return;
Packit eace71
	}
Packit eace71
	if (logout_resp.response != ISCSI_LOGOUT_SUCCESS) {
Packit eace71
		log_error("iscsid: logout failed - response = 0x%x",
Packit eace71
		       logout_resp.response);
Packit eace71
	}
Packit eace71
}
Packit eace71
#endif /* Unused */
Packit eace71
Packit eace71
static void iscsi_destroy_session(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	struct iscsi_transport *t = session->t;
Packit eace71
	struct iscsi_conn *conn = &session->conn[0];
Packit eace71
	int rc;
Packit eace71
Packit eace71
	if (session->id == -1)
Packit eace71
		return;
Packit eace71
Packit eace71
	if (!(t->caps & CAP_TEXT_NEGO)) {
Packit eace71
		iscsi_io_disconnect(&session->conn[0]);
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(2, "%s ep disconnect", __FUNCTION__);
Packit eace71
	t->template->ep_disconnect(conn);
Packit eace71
Packit eace71
	log_debug(2, "stop conn");
Packit eace71
	rc = ipc->stop_conn(session->t->handle, session->id,
Packit eace71
			   conn->id, STOP_CONN_TERM);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not stop conn %d:%d cleanly (err %d)",
Packit eace71
			  session->id, conn->id, rc);
Packit eace71
		goto done;
Packit eace71
        }
Packit eace71
Packit eace71
	log_debug(2, "%s destroy conn", __FUNCTION__);
Packit eace71
        rc = ipc->destroy_conn(session->t->handle, session->id, conn->id);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not safely destroy conn %d:%d (err %d)",
Packit eace71
			  session->id, conn->id, rc);
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(2, "%s destroy session", __FUNCTION__);
Packit eace71
	rc = ipc->destroy_session(session->t->handle, session->id);
Packit eace71
	if (rc)
Packit eace71
		log_error("Could not safely destroy session %d (err %d)",
Packit eace71
			  session->id, rc);
Packit eace71
done:
Packit eace71
	if (session->target_alias) {
Packit eace71
	    free(session->target_alias);
Packit eace71
	    session->target_alias = NULL;
Packit eace71
	}
Packit eace71
Packit eace71
	if (conn->socket_fd >= 0) {
Packit eace71
		ipc->ctldev_close();
Packit eace71
		conn->socket_fd = -1;
Packit eace71
	}
Packit eace71
	session->id = -1;
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_create_leading_conn(struct iscsi_session *session)
Packit eace71
{
Packit eace71
	struct iface_rec *iface = &session->nrec.iface;
Packit eace71
	struct iscsi_transport *t = session->t;
Packit eace71
	struct iscsi_conn *conn = &session->conn[0];
Packit eace71
	uint32_t host_no;
Packit eace71
	int rc, sleep_count = 0;
Packit eace71
Packit eace71
	if (!(t->caps & CAP_TEXT_NEGO)) {
Packit eace71
		/*
Packit eace71
		 * If the LLD does not support TEXT PDUs then we do
Packit eace71
		 * discovery in userspace.
Packit eace71
		 */
Packit eace71
		session->use_ipc = 0;
Packit eace71
Packit eace71
		if (!iscsi_io_connect(conn))
Packit eace71
			return ISCSI_ERR_TRANS;
Packit eace71
Packit eace71
		session->id = 1;
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
	session->use_ipc = 1;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * for software this is the tcp socket fd set in iscsi_io_connect
Packit eace71
	 * and for offload this is the iscsi netlink socket fd
Packit eace71
	 */
Packit eace71
	conn->socket_fd = ipc->ctldev_open();
Packit eace71
	if (conn->socket_fd < 0) {
Packit eace71
		log_error("Could not open netlink interface (err %d)",
Packit eace71
			  errno);
Packit eace71
		return ISCSI_ERR_INTERNAL;
Packit eace71
	}
Packit eace71
Packit eace71
	host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc);
Packit eace71
	if (!rc) {
Packit eace71
		/*
Packit eace71
		 * if the netdev or mac was set, then we are going to want
Packit eace71
		 * to want to bind the all the conns/eps to a specific host
Packit eace71
		 * if offload is used.
Packit eace71
		 */
Packit eace71
		session->conn[0].bind_ep = 1;
Packit eace71
		session->hostno = host_no;
Packit eace71
	}
Packit eace71
Packit eace71
	rc = iscsi_host_set_net_params(iface, session);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not set host net params (err %d)",
Packit eace71
			  rc);
Packit eace71
		if (rc != ISCSI_ERR_AGAIN)
Packit eace71
			rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto close_ipc;
Packit eace71
	}
Packit eace71
Packit eace71
	/* create interconnect endpoint */
Packit eace71
	log_debug(2, "%s discovery ep connect", __FUNCTION__);
Packit eace71
	rc = t->template->ep_connect(conn, 1);
Packit eace71
	if (rc < 0) {
Packit eace71
		rc = ISCSI_ERR_TRANS;
Packit eace71
		goto close_ipc;
Packit eace71
	}
Packit eace71
Packit eace71
	do {
Packit eace71
		rc = t->template->ep_poll(conn, 1);
Packit eace71
		if (rc < 0) {
Packit eace71
			rc = ISCSI_ERR_TRANS;
Packit eace71
			goto disconnect;
Packit eace71
		} else if (rc == 0) {
Packit eace71
			if (sleep_count == conn->login_timeout) {
Packit eace71
				rc = ISCSI_ERR_TRANS_TIMEOUT;
Packit eace71
				goto disconnect;
Packit eace71
			}
Packit eace71
			sleep_count++;
Packit eace71
			sleep(1);
Packit eace71
		} else
Packit eace71
			break;
Packit eace71
	} while (1);
Packit eace71
Packit eace71
	log_debug(2, "%s discovery create session", __FUNCTION__);
Packit eace71
	/* create kernel structs */
Packit eace71
        rc = ipc->create_session(session->t->handle,
Packit eace71
				 conn->transport_ep_handle, 1, 32, 1,
Packit eace71
				 &session->id, &host_no);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not create kernel session (err %d).", rc);
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto disconnect;
Packit eace71
	}
Packit eace71
	log_debug(2, "%s discovery created session %u", __FUNCTION__,
Packit eace71
		  session->id);
Packit eace71
	session->isid[3] = (session->id >> 16) & 0xff;
Packit eace71
	session->isid[4] = (session->id >>  8) & 0xff;
Packit eace71
	session->isid[5] = session->id & 0xff;
Packit eace71
Packit eace71
	log_debug(2, "%s discovery create conn", __FUNCTION__);
Packit eace71
	rc = ipc->create_conn(t->handle, session->id, conn->id, &conn->id);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not create connection (err %d)", rc);
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto disconnect;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(2, "%s discovery bind conn", __FUNCTION__);
Packit eace71
	if (ipc->bind_conn(t->handle, session->id, conn->id,
Packit eace71
			   conn->transport_ep_handle, (conn->id == 0), &rc) ||
Packit eace71
	    rc) {
Packit eace71
		log_error("Could not bind conn %d:%d to session %d, "
Packit eace71
			  "(err %d)", session->id, conn->id,
Packit eace71
			  session->id, rc);
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto disconnect;
Packit eace71
	}
Packit eace71
Packit eace71
	/* all set */
Packit eace71
	return 0;
Packit eace71
Packit eace71
disconnect:
Packit eace71
	t->template->ep_disconnect(conn);
Packit eace71
Packit eace71
	if (session->id != -1 &&
Packit eace71
	    iscsi_sysfs_session_has_leadconn(session->id)) {
Packit eace71
		if (ipc->destroy_conn(session->t->handle, session->id,
Packit eace71
				       conn->id))
Packit eace71
			log_error("Could not safely destroy connection %d:%d",
Packit eace71
				  session->id, conn->id);
Packit eace71
	}
Packit eace71
Packit eace71
	if (session->id != -1) {
Packit eace71
		if (ipc->destroy_session(session->t->handle, session->id))
Packit eace71
			log_error("Could not safely destroy session %d",
Packit eace71
				  session->id);
Packit eace71
		session->id = -1;
Packit eace71
	}
Packit eace71
Packit eace71
close_ipc:
Packit eace71
	if (conn->socket_fd >= 0) {
Packit eace71
		ipc->ctldev_close();
Packit eace71
		conn->socket_fd = -1;
Packit eace71
	}
Packit eace71
Packit eace71
	log_error("Connection to discovery portal %s failed: %s",
Packit eace71
		  conn->host, iscsi_err_to_str(rc));
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static struct iscsi_ev_context *
Packit eace71
iscsi_ev_context_get(struct iscsi_conn *conn, int ev_size)
Packit eace71
{
Packit eace71
	log_debug(2, "%s: ev_size %d", __FUNCTION__, ev_size);
Packit eace71
Packit eace71
	ipc_ev_context.data = calloc(1, ev_size);
Packit eace71
	if (!ipc_ev_context.data)
Packit eace71
		return NULL;
Packit eace71
Packit eace71
	return &ipc_ev_context;
Packit eace71
}
Packit eace71
Packit eace71
static void iscsi_ev_context_put(struct iscsi_ev_context *ev_context)
Packit eace71
{
Packit eace71
	if (ev_context->data)
Packit eace71
		free(ev_context->data);
Packit eace71
	ev_context->data = NULL;
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context,
Packit eace71
				  struct iscsi_conn *conn, unsigned long tmo,
Packit eace71
				  int event)
Packit eace71
{
Packit eace71
	if (event == EV_CONN_RECV_PDU || event == EV_CONN_LOGIN) {
Packit eace71
		conn->recv_context = ev_context;
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	return -EIO;
Packit eace71
}
Packit eace71
Packit eace71
static struct iscsi_ipc_ev_clbk ipc_clbk = {
Packit eace71
        .get_ev_context         = iscsi_ev_context_get,
Packit eace71
        .put_ev_context         = iscsi_ev_context_put,
Packit eace71
        .sched_ev_context       = iscsi_sched_ev_context,
Packit eace71
};
Packit eace71
Packit eace71
static int iscsi_wait_for_login(struct iscsi_conn *conn)
Packit eace71
{
Packit eace71
	struct iscsi_session *session = conn->session;
Packit eace71
	struct iscsi_transport *t = session->t;
Packit eace71
	struct pollfd pfd;
Packit eace71
	struct timeval connection_timer;
Packit eace71
	int timeout, rc;
Packit eace71
	uint32_t conn_state;
Packit eace71
	int status = 0;
Packit eace71
Packit eace71
	if (!(t->caps & CAP_LOGIN_OFFLOAD))
Packit eace71
		return 0;
Packit eace71
Packit eace71
	iscsi_timer_set(&connection_timer, conn->active_timeout);
Packit eace71
Packit eace71
	/* prepare to poll */
Packit eace71
	memset(&pfd, 0, sizeof(pfd));
Packit eace71
	pfd.fd = conn->socket_fd;
Packit eace71
	pfd.events = POLLIN | POLLPRI;
Packit eace71
Packit eace71
	timeout = iscsi_timer_msecs_until(&connection_timer);
Packit eace71
Packit eace71
login_repoll:
Packit eace71
	log_debug(4, "discovery login process polling fd %d, "
Packit eace71
		 "timeout in %f seconds", pfd.fd, timeout / 1000.0);
Packit eace71
Packit eace71
	pfd.revents = 0;
Packit eace71
	rc = poll(&pfd, 1, timeout);
Packit eace71
Packit eace71
	log_debug(7, "discovery login process returned from poll, rc %d", rc);
Packit eace71
Packit eace71
	if (iscsi_timer_expired(&connection_timer)) {
Packit eace71
		log_warning("Discovery login session timed out.");
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	if (rc > 0) {
Packit eace71
		if (pfd.revents & (POLLIN | POLLPRI)) {
Packit eace71
			timeout = iscsi_timer_msecs_until(&connection_timer);
Packit eace71
			status = ipc->recv_conn_state(conn, &conn_state);
Packit eace71
			if (status == -EAGAIN)
Packit eace71
				goto login_repoll;
Packit eace71
			else if (status < 0) {
Packit eace71
				rc = ISCSI_ERR_TRANS;
Packit eace71
				goto done;
Packit eace71
			}
Packit eace71
Packit eace71
			if (conn_state != ISCSI_CONN_STATE_LOGGED_IN)
Packit eace71
				rc = ISCSI_ERR_TRANS;
Packit eace71
			else
Packit eace71
				rc = 0;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLHUP) {
Packit eace71
			log_warning("discovery session"
Packit eace71
				    "terminating after hangup");
Packit eace71
			 rc = ISCSI_ERR_TRANS;
Packit eace71
			 goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLNVAL) {
Packit eace71
			log_warning("discovery POLLNVAL");
Packit eace71
			rc = ISCSI_ERR_INTERNAL;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLERR) {
Packit eace71
			log_warning("discovery POLLERR");
Packit eace71
			rc = ISCSI_ERR_INTERNAL;
Packit eace71
			goto done;
Packit eace71
		}
Packit eace71
	} else if (rc < 0) {
Packit eace71
		log_error("Login poll error");
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
done:
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static int iscsi_create_session(struct iscsi_session *session,
Packit eace71
				struct iscsi_sendtargets_config *config,
Packit eace71
				char *data, unsigned int data_len)
Packit eace71
{
Packit eace71
	struct iscsi_conn *conn = &session->conn[0];
Packit eace71
	int login_status, rc = 0, login_delay = 0;
Packit eace71
	uint8_t status_class = 0, status_detail = 0;
Packit eace71
	unsigned int login_failures = 0;
Packit eace71
	char serv[NI_MAXSERV];
Packit eace71
	struct iscsi_transport *t = session->t;
Packit eace71
Packit eace71
set_address:
Packit eace71
	/*
Packit eace71
	 * copy the saved address to the session,
Packit eace71
	 * undoing any temporary redirect
Packit eace71
	 */
Packit eace71
	conn->saddr = conn->failback_saddr;
Packit eace71
Packit eace71
reconnect:
Packit eace71
	/* fix decrement and test */
Packit eace71
	if (--session->reopen_cnt < 0) {
Packit eace71
		log_error("connection login retries (reopen_max) %d exceeded",
Packit eace71
			  config->reopen_max);
Packit eace71
		rc = ISCSI_ERR_PDU_TIMEOUT;
Packit eace71
		goto login_failed;
Packit eace71
	}
Packit eace71
Packit eace71
redirect_reconnect:
Packit eace71
	session->cmdsn = 1;
Packit eace71
	session->itt = 1;
Packit eace71
	session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * On reconnect, just destroy the kernel structs and start over.
Packit eace71
	 */
Packit eace71
	iscsi_destroy_session(session);
Packit eace71
Packit eace71
	/* slowly back off the frequency of login attempts */
Packit eace71
	if (login_failures == 0)
Packit eace71
		login_delay = 0;
Packit eace71
	else if (login_failures < 10)
Packit eace71
		login_delay = 1;	/* 10 seconds at 1 sec each */
Packit eace71
	else if (login_failures < 20)
Packit eace71
		login_delay = 2;	/* 20 seconds at 2 sec each */
Packit eace71
	else if (login_failures < 26)
Packit eace71
		login_delay = 5;	/* 30 seconds at 5 sec each */
Packit eace71
	else if (login_failures < 34)
Packit eace71
		login_delay = 15;	/* 60 seconds at 15 sec each */
Packit eace71
	else
Packit eace71
		login_delay = 60;	/* after 2 minutes, try once a minute */
Packit eace71
Packit eace71
	getnameinfo((struct sockaddr *) &conn->saddr,
Packit eace71
		    sizeof(conn->saddr), conn->host,
Packit eace71
		    sizeof(conn->host), serv, sizeof(serv),
Packit eace71
		    NI_NUMERICHOST|NI_NUMERICSERV);
Packit eace71
Packit eace71
	if (login_delay) {
Packit eace71
		log_debug(4, "discovery session to %s:%s sleeping for %d "
Packit eace71
			 "seconds before next login attempt",
Packit eace71
			 conn->host, serv, login_delay);
Packit eace71
		sleep(login_delay);
Packit eace71
	}
Packit eace71
	rc = iscsi_create_leading_conn(session);
Packit eace71
	if (rc) {
Packit eace71
		login_failures++;
Packit eace71
		goto reconnect;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(1, "connected to discovery address %s", conn->host);
Packit eace71
Packit eace71
	log_debug(4, "discovery session to %s:%s starting iSCSI login",
Packit eace71
		 conn->host, serv);
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Need to re-init settings because a previous login could
Packit eace71
	 * have set them to what was negotiated for.
Packit eace71
	 */
Packit eace71
	iscsi_copy_operational_params(&session->conn[0], &config->session_conf,
Packit eace71
				      &config->conn_conf);
Packit eace71
Packit eace71
	if (t->caps & CAP_TEXT_NEGO) {
Packit eace71
		log_debug(2, "%s discovery set params", __FUNCTION__);
Packit eace71
		rc = iscsi_session_set_params(conn);
Packit eace71
		if (rc) {
Packit eace71
			log_error("Could not set iscsi params for conn %d:%d "
Packit eace71
				  "(err %d)", session->id, conn->id, rc);
Packit eace71
			rc = ISCSI_ERR_INTERNAL;
Packit eace71
			goto login_failed;
Packit eace71
		}
Packit eace71
	}
Packit eace71
Packit eace71
	if ((session->t->caps & CAP_LOGIN_OFFLOAD))
Packit eace71
		goto start_conn;
Packit eace71
Packit eace71
	status_class = 0;
Packit eace71
	status_detail = 0;
Packit eace71
	rc = ISCSI_ERR_LOGIN;
Packit eace71
Packit eace71
	memset(data, 0, data_len);
Packit eace71
	login_status = iscsi_login(session, 0, data, data_len,
Packit eace71
				   &status_class, &status_detail);
Packit eace71
Packit eace71
	switch (login_status) {
Packit eace71
	case LOGIN_OK:
Packit eace71
	case LOGIN_REDIRECT:
Packit eace71
		break;
Packit eace71
Packit eace71
	case LOGIN_IO_ERROR:
Packit eace71
	case LOGIN_REDIRECTION_FAILED:
Packit eace71
		/* try again */
Packit eace71
		log_warning("retrying discovery login to %s", conn->host);
Packit eace71
		login_failures++;
Packit eace71
		goto set_address;
Packit eace71
Packit eace71
	default:
Packit eace71
	case LOGIN_FAILED:
Packit eace71
	case LOGIN_NEGOTIATION_FAILED:
Packit eace71
	case LOGIN_AUTHENTICATION_FAILED:
Packit eace71
	case LOGIN_VERSION_MISMATCH:
Packit eace71
	case LOGIN_INVALID_PDU:
Packit eace71
		log_error("discovery login to %s failed, giving up %d",
Packit eace71
			  conn->host, login_status);
Packit eace71
		rc = ISCSI_ERR_FATAL_LOGIN;
Packit eace71
		goto login_failed;
Packit eace71
	}
Packit eace71
Packit eace71
	/* check the login status */
Packit eace71
	switch (status_class) {
Packit eace71
	case ISCSI_STATUS_CLS_SUCCESS:
Packit eace71
		log_debug(4, "discovery login success to %s", conn->host);
Packit eace71
		login_failures = 0;
Packit eace71
		break;
Packit eace71
	case ISCSI_STATUS_CLS_REDIRECT:
Packit eace71
		switch (status_detail) {
Packit eace71
			/* the session IP address was changed by the login
Packit eace71
			 * library, so just try again with this portal
Packit eace71
			 * config but the new address.
Packit eace71
			 */
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP:
Packit eace71
			log_warning(
Packit eace71
				"discovery login temporarily redirected to "
Packit eace71
				"%s port %s", conn->host, serv);
Packit eace71
			goto redirect_reconnect;
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM:
Packit eace71
			log_warning(
Packit eace71
				"discovery login permanently redirected to "
Packit eace71
				"%s port %s", conn->host, serv);
Packit eace71
			/* make the new address permanent */
Packit eace71
			memset(&conn->failback_saddr, 0,
Packit eace71
				sizeof(struct sockaddr_storage));
Packit eace71
			conn->failback_saddr = conn->saddr;
Packit eace71
			goto redirect_reconnect;
Packit eace71
		default:
Packit eace71
			log_error(
Packit eace71
			       "discovery login rejected: redirection type "
Packit eace71
			       "0x%x not supported",
Packit eace71
			       status_detail);
Packit eace71
			goto set_address;
Packit eace71
		}
Packit eace71
		break;
Packit eace71
	case ISCSI_STATUS_CLS_INITIATOR_ERR:
Packit eace71
		switch (status_detail) {
Packit eace71
		case ISCSI_LOGIN_STATUS_AUTH_FAILED:
Packit eace71
		case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN:
Packit eace71
			log_error("discovery login to %s rejected: "
Packit eace71
				  "initiator failed authorization",
Packit eace71
				 conn->host);
Packit eace71
			rc = ISCSI_ERR_LOGIN_AUTH_FAILED;
Packit eace71
			goto login_failed;
Packit eace71
		default:
Packit eace71
			log_error("discovery login to %s rejected: initiator "
Packit eace71
				  "error (%02x/%02x), non-retryable, giving up",
Packit eace71
				  conn->host, status_class, status_detail);
Packit eace71
			rc = ISCSI_ERR_FATAL_LOGIN;
Packit eace71
		}
Packit eace71
		goto login_failed;
Packit eace71
	case ISCSI_STATUS_CLS_TARGET_ERR:
Packit eace71
		log_error(
Packit eace71
			"discovery login to %s rejected: "
Packit eace71
			"target error (%02x/%02x)",
Packit eace71
			conn->host, status_class, status_detail);
Packit eace71
		login_failures++;
Packit eace71
		goto reconnect;
Packit eace71
	default:
Packit eace71
		log_error(
Packit eace71
			"discovery login to %s failed, response "
Packit eace71
			"with unknown status class 0x%x, detail 0x%x",
Packit eace71
			conn->host,
Packit eace71
			status_class, status_detail);
Packit eace71
		login_failures++;
Packit eace71
		goto reconnect;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!(t->caps & CAP_TEXT_NEGO))
Packit eace71
		return 0;
Packit eace71
Packit eace71
start_conn:
Packit eace71
	log_debug(2, "%s discovery set neg params", __FUNCTION__);
Packit eace71
	rc = iscsi_session_set_neg_params(conn);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not set iscsi params for conn %d:%d (err "
Packit eace71
			  "%d)", session->id, conn->id, rc);
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto login_failed;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(2, "%s discovery start conn", __FUNCTION__);
Packit eace71
	if (ipc->start_conn(t->handle, session->id, conn->id, &rc) || rc) {
Packit eace71
		log_error("Cannot start conn %d:%d (err %d)",
Packit eace71
			  session->id, conn->id, rc);
Packit eace71
		rc = ISCSI_ERR_INTERNAL;
Packit eace71
		goto login_failed;
Packit eace71
	}
Packit eace71
Packit eace71
	rc = iscsi_wait_for_login(conn);
Packit eace71
	if (!rc)
Packit eace71
		return 0;
Packit eace71
Packit eace71
login_failed:
Packit eace71
	iscsi_destroy_session(session);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
int discovery_sendtargets(void *fndata, struct iface_rec *iface,
Packit eace71
			  struct list_head *rec_list)
Packit eace71
{
Packit eace71
	discovery_rec_t *drec = fndata;
Packit eace71
	iscsi_session_t *session;
Packit eace71
	struct pollfd pfd;
Packit eace71
	struct iscsi_hdr pdu_buffer;
Packit eace71
	struct iscsi_hdr *pdu = &pdu_buffer;
Packit eace71
	char *data = NULL;
Packit eace71
	int active = 0, valid_text = 0;
Packit eace71
	struct timeval connection_timer;
Packit eace71
	int timeout;
Packit eace71
	int rc = 0;
Packit eace71
	struct str_buffer sendtargets;
Packit eace71
	unsigned int data_len;
Packit eace71
	struct iscsi_sendtargets_config *config = &drec->u.sendtargets;
Packit eace71
Packit eace71
	/* initial setup */
Packit eace71
	log_debug(1, "starting sendtargets discovery, address %s:%d, ",
Packit eace71
		 drec->address, drec->port);
Packit eace71
	memset(&pdu_buffer, 0, sizeof (pdu_buffer));
Packit eace71
	iscsi_timer_clear(&connection_timer);
Packit eace71
Packit eace71
	/* allocate a new session, and initialize default values */
Packit eace71
	session = iscsi_alloc_session(config, iface, &rc, drec->iscsid_req_tmo);
Packit eace71
	if (rc)
Packit eace71
		return rc;
Packit eace71
Packit eace71
	ipc_ev_context.conn = &session->conn[0];
Packit eace71
	ipc_register_ev_callback(&ipc_clbk);
Packit eace71
Packit eace71
	log_debug(4, "sendtargets discovery to %s:%d using "
Packit eace71
		 "isid 0x%02x%02x%02x%02x%02x%02x",
Packit eace71
		 drec->address, drec->port, session->isid[0],
Packit eace71
		 session->isid[1], session->isid[2], session->isid[3],
Packit eace71
		 session->isid[4], session->isid[5]);
Packit eace71
Packit eace71
	/* allocate data buffers for SendTargets data */
Packit eace71
	data = malloc(session->conn[0].max_recv_dlength);
Packit eace71
	if (!data) {
Packit eace71
		rc = ISCSI_ERR_NOMEM;
Packit eace71
		goto free_session;
Packit eace71
	}
Packit eace71
	data_len = session->conn[0].max_recv_dlength;
Packit eace71
Packit eace71
	str_init_buffer(&sendtargets, 0);
Packit eace71
Packit eace71
	/* resolve the DiscoveryAddress to an IP address */
Packit eace71
	rc = iscsi_setup_portal(&session->conn[0], drec->address,
Packit eace71
				drec->port);
Packit eace71
	if (rc) {
Packit eace71
		log_error("cannot resolve host name %s", drec->address);
Packit eace71
		goto free_sendtargets;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.",
Packit eace71
		 session->conn[0].login_timeout, session->reopen_cnt,
Packit eace71
		 session->conn[0].auth_timeout);
Packit eace71
Packit eace71
reconnect:
Packit eace71
	rc = iscsi_create_session(session, &drec->u.sendtargets,
Packit eace71
				  data, data_len);
Packit eace71
	if (rc)
Packit eace71
		goto free_sendtargets;
Packit eace71
Packit eace71
	/* reinitialize */
Packit eace71
	str_truncate_buffer(&sendtargets, 0);
Packit eace71
Packit eace71
	/* ask for targets */
Packit eace71
	if (!request_targets(session)) {
Packit eace71
		goto reconnect;
Packit eace71
	}
Packit eace71
	active = 1;
Packit eace71
Packit eace71
	/* set timeouts */
Packit eace71
	iscsi_timer_set(&connection_timer, session->conn[0].active_timeout);
Packit eace71
Packit eace71
	/* prepare to poll */
Packit eace71
	memset(&pfd, 0, sizeof (pfd));
Packit eace71
	pfd.fd = session->conn[0].socket_fd;
Packit eace71
	pfd.events = POLLIN | POLLPRI;
Packit eace71
Packit eace71
repoll:
Packit eace71
	timeout = iscsi_timer_msecs_until(&connection_timer);
Packit eace71
	/* block until we receive a PDU, a TCP FIN, a TCP RST,
Packit eace71
	 * or a timeout
Packit eace71
	 */
Packit eace71
	log_debug(4,
Packit eace71
		 "discovery process  %s:%d polling fd %d, "
Packit eace71
		 "timeout in %f seconds",
Packit eace71
		 drec->address, drec->port, pfd.fd,
Packit eace71
		 timeout / 1000.0);
Packit eace71
Packit eace71
	pfd.revents = 0;
Packit eace71
	rc = poll(&pfd, 1, timeout);
Packit eace71
Packit eace71
	log_debug(7,
Packit eace71
		 "discovery process to %s:%d returned from poll, rc %d",
Packit eace71
		 drec->address, drec->port, rc);
Packit eace71
Packit eace71
	if (iscsi_timer_expired(&connection_timer)) {
Packit eace71
		log_warning("Discovery session to %s:%d timed out.",
Packit eace71
			    drec->address, drec->port);
Packit eace71
		rc = ISCSI_ERR_TRANS_TIMEOUT;
Packit eace71
		goto reconnect;
Packit eace71
	}
Packit eace71
Packit eace71
	if (rc > 0) {
Packit eace71
		if (pfd.revents & (POLLIN | POLLPRI)) {
Packit eace71
			timeout = iscsi_timer_msecs_until(&connection_timer);
Packit eace71
Packit eace71
			rc = iscsi_io_recv_pdu(&session->conn[0],
Packit eace71
					        pdu, ISCSI_DIGEST_NONE, data,
Packit eace71
					        data_len, ISCSI_DIGEST_NONE,
Packit eace71
					        timeout);
Packit eace71
			if (rc == -EAGAIN)
Packit eace71
				goto repoll;
Packit eace71
			else if (rc < 0) {
Packit eace71
				log_debug(1, "discovery session to "
Packit eace71
					  "%s:%d failed to recv a PDU "
Packit eace71
					  "response, terminating",
Packit eace71
					   drec->address,
Packit eace71
					   drec->port);
Packit eace71
				rc = ISCSI_ERR_PDU_TIMEOUT;
Packit eace71
				goto free_sendtargets;
Packit eace71
			}
Packit eace71
Packit eace71
			/*
Packit eace71
			 * process iSCSI PDU received
Packit eace71
			 */
Packit eace71
			rc = process_recvd_pdu(pdu, drec, rec_list,
Packit eace71
					       session, &sendtargets,
Packit eace71
					       &active, &valid_text, data);
Packit eace71
			if (rc == DISCOVERY_NEED_RECONNECT)
Packit eace71
				goto reconnect;
Packit eace71
Packit eace71
			/* reset timers after receiving a PDU */
Packit eace71
			if (active) {
Packit eace71
				iscsi_timer_set(&connection_timer,
Packit eace71
				       session->conn[0].active_timeout);
Packit eace71
				goto repoll;
Packit eace71
			}
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLHUP) {
Packit eace71
			log_warning("discovery session to %s:%d "
Packit eace71
				    "terminating after hangup",
Packit eace71
				     drec->address, drec->port);
Packit eace71
			rc = ISCSI_ERR_TRANS;
Packit eace71
			goto free_sendtargets;
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLNVAL) {
Packit eace71
			log_warning("discovery POLLNVAL");
Packit eace71
			sleep(1);
Packit eace71
			goto reconnect;
Packit eace71
		}
Packit eace71
Packit eace71
		if (pfd.revents & POLLERR) {
Packit eace71
			log_warning("discovery POLLERR");
Packit eace71
			sleep(1);
Packit eace71
			goto reconnect;
Packit eace71
		}
Packit eace71
	} else if (rc < 0) {
Packit eace71
		log_error("poll error");
Packit eace71
		rc = ISCSI_ERR;
Packit eace71
		goto free_sendtargets;
Packit eace71
	}
Packit eace71
Packit eace71
	log_debug(1, "discovery process to %s:%d exiting",
Packit eace71
		 drec->address, drec->port);
Packit eace71
	rc = 0;
Packit eace71
Packit eace71
free_sendtargets:
Packit eace71
	str_free_buffer(&sendtargets);
Packit eace71
	free(data);
Packit eace71
	iscsi_destroy_session(session);
Packit eace71
free_session:
Packit eace71
	iscsi_free_session(session);
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
#ifdef SLP_ENABLE
Packit eace71
int
Packit eace71
slp_discovery(struct iscsi_slp_config *config)
Packit eace71
{
Packit eace71
	struct sigaction action;
Packit eace71
	char *pl;
Packit eace71
	unsigned short flag = 0;
Packit eace71
Packit eace71
	memset(&action, 0, sizeof (struct sigaction));
Packit eace71
	action.sa_sigaction = NULL;
Packit eace71
	action.sa_flags = 0;
Packit eace71
	action.sa_handler = SIG_DFL;
Packit eace71
	sigaction(SIGTERM, &action, NULL);
Packit eace71
	sigaction(SIGINT, &action, NULL);
Packit eace71
	sigaction(SIGPIPE, &action, NULL);
Packit eace71
Packit eace71
	action.sa_handler = sighup_handler;
Packit eace71
	sigaction(SIGHUP, &action, NULL);
Packit eace71
Packit eace71
	if (iscsi_process_should_exit()) {
Packit eace71
		log_debug(1, "slp discovery process %p exiting", discovery);
Packit eace71
		exit(0);
Packit eace71
	}
Packit eace71
Packit eace71
	discovery->pid = getpid();
Packit eace71
Packit eace71
	pl = generate_predicate_list(discovery, &flag;;
Packit eace71
Packit eace71
	while (1) {
Packit eace71
		if (flag == SLP_MULTICAST_ENABLED) {
Packit eace71
			discovery->flag = SLP_MULTICAST_ENABLED;
Packit eace71
			slp_multicast_srv_query(discovery, pl, GENERIC_QUERY);
Packit eace71
		}
Packit eace71
Packit eace71
		if (flag == SLP_UNICAST_ENABLED) {
Packit eace71
			discovery->flag = SLP_UNICAST_ENABLED;
Packit eace71
			slp_unicast_srv_query(discovery, pl, GENERIC_QUERY);
Packit eace71
		}
Packit eace71
Packit eace71
		sleep(config->poll_interval);
Packit eace71
	}
Packit eace71
Packit eace71
	exit(0);
Packit eace71
}
Packit eace71
Packit eace71
#endif